Railway Operation Simulator  v2.6.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
63  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
64  int IncrementalDigitsIn, int SignallerMaxSpeedIn): RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
65  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
66  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
67  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
68 /*
69  Construct a new train with general default values and input values for position and headcode.
70  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
71  This is because trains are kept in a vector and vectors erase elements during internal operations.
72  Deletion is explicit by using a special function. Increment the static class member NextTrainID
73  after setting this train's ID.
74 */
75 
76 {
77  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
78  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
79  AnsiString(TrainModeIn));
80  // AutoControl = true;//all trains start in auto control
81  UpdateCounter = 0;
82  TimeTimeLocArrived = false;
83  Derailed = false;
84  DerailPending = false;
85  Crashed = false;
86  StoppedAtBuffers = false;
87  StoppedAtSignal = false;
88  StoppedAtLocation = false;
89  StoppedAfterSPAD = false;
90  StoppedWithoutPower = false; // new at v2.4.0
91  StoppedForTrainInFront = false;
92  SignallerStoppingFlag = false;
93  SignallerStopped = false;
94  SignallerRemoved = false;
95  NotInService = false;
96  HoldAtLocationInTTMode = false;
97  AllowedToPassRedSignal = false;
98  CallingOnFlag = false;
99  BeingCalledOn = false;
100  DepartureTimeSet = false;
102  TimetableFinished = false;
103  LastActionDelayFlag = false;
104  OneLengthAccelDecel = false;
105  TrainCrashedInto = -1;
107  Plotted = false;
108  TrainGone = false;
109  SPADFlag = false;
110  FrontCodePtr = new Graphics::TBitmap;
111  FrontCodePtr->PixelFormat = pf8bit;
112  FrontCodePtr->Height = 8;
113  FrontCodePtr->Width = 8;
115  FrontCodePtr->Transparent = false;
116  AValue = sqrt(2 * PowerAtRail / Mass);
118  TerminatedMessageSent = false;
119  JoinedOtherTrainFlag = false;
121  StepForwardFlag = false;
123  for(int x = 0; x < 4; x++)
124  {
125  HeadCodeGrPtr[x] = new Graphics::TBitmap;
126  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
127  HeadCodeGrPtr[x]->Height = 8;
128  HeadCodeGrPtr[x]->Width = 8;
130  HeadCodeGrPtr[x]->Transparent = false;
131  }
132  for(int x = 0; x < 4; x++)
133  {
134  BackgroundPtr[x] = new Graphics::TBitmap;
135  BackgroundPtr[x]->PixelFormat = pf8bit;
136  BackgroundPtr[x]->Height = 8;
137  BackgroundPtr[x]->Width = 8;
139  BackgroundPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
144  // set here to ensure have values
145  }
146  for(int x = 0; x < 4; x++)
147  {
148  PlotElement[x] = -1; // marker for not plotted yet
149  }
150  for(int x = 0; x < 3; x++)
151  {
152  OldZoomOutElement[x] = -1; // marker for not plotted yet
153  }
155  NextTrainID++;
156 
157  // new values added to complete initialisation of all TTrain variables
158 
159  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
160  // TrainDataEntryPtr, initialise in AddTrain
162  FrontElementLength = 0;
163  EntrySpeed = 0;
164  ExitSpeedHalf = 0;
165  ExitSpeedFull = 0;
166  MaxExitSpeed = 0;
167  BrakeRate = 0;
169  FirstHalfMove = true;
170  EntryTime = 0;
171  ExitTimeHalf = 0;
172  ExitTimeFull = 0;
173  ReleaseTime = 0;
174  TRSTime = 0;
175  LastActionTime = 0;
176  Straddle = MidLag;
177  LeadElement = -1;
178  LeadEntryPos = 0;
179  LeadExitPos = 0;
180  MidElement = -1;
181  MidEntryPos = 0;
182  MidExitPos = 0;
183  LagElement = -1;
184  LagEntryPos = 0;
185  LagExitPos = 0;
186  TrainFailed = false; // added at v2.4.0
187  for(int x = 0; x < 4; x++)
188  {
189  HOffset[x] = 0;
190  VOffset[x] = 0;
191  PlotEntryPos[x] = 0;
192  }
193  OpTimeToAct = 60; // default value, new at v2.2.0
194  MinsDelayed = 0.0; // new at v2.2.0
195  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
196  FinishJoinLogSent = false;
197  // added at v2.4.0 to prevent repeatdly logging the event
200  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
204  ZeroPowerNoCDTMessage = false;
209  TrainFailurePending = false;
210  Utilities->CallLogPop(648);
211 }
212 
213 // ---------------------------------------------------------------------------
214 
215 void TTrain::DeleteTrain(int Caller)
216 /*
217  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
218  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
219  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
220  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
221  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
222  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
223  No need to delete HeadCodePosition as that just points to existing bitmaps
224 */ {
225  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
226  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
228  if(Display->ZoomOutFlag)
230  if(FrontCodePtr == 0)
231  {
232  throw Exception("Error in attempting to delete FrontCodePtr");
233  }
234  delete FrontCodePtr;
235  FrontCodePtr = 0;
236  for(int x = 0; x < 4; x++)
237  {
238  if(BackgroundPtr[x] == 0)
239  {
240  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
241  }
242  delete BackgroundPtr[x];
243  BackgroundPtr[x] = 0;
244  }
245  for(int x = 0; x < 4; x++)
246  {
247  if(HeadCodeGrPtr[x] == 0)
248  {
249  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
250  }
251  delete HeadCodeGrPtr[x];
252  HeadCodeGrPtr[x] = 0;
253  }
254  Utilities->CallLogPop(649);
255 }
256 
257 // ---------------------------------------------------------------------------
258 
260 /*
261  Plots the train starting position on screen. Note that the check for starting on straight points &
262  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
263  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
264  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
265  Set the headcode graphics pointers from the headcode text, then check whether starting at a
266  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
267  for the continuation element. Otherwise set Lead and Mid values,
268 
269  and Lead element value unless
270  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
271  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
272  then check if a train on either Mid or Lag and if so give a warning message and return false so
273  that the calling function can delete the train. Plot the Mid element train values then do similarly
274  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
275  the train. Finally set the Plotted flag and return true.
276 */ {
277  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
278  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
279 
281  // PlotStartTime = TrainController->TTClockTime;
282  FirstHalfMove = true;
283 
284  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
285  // 'claim' it for this train to prevent any other waiting trains trying to enter
287  {
288  LagElement = -1; // not to be plotted
289  LagExitPos = 0; // not to be plotted
290  LagEntryPos = 0; // not to be plotted
291  MidElement = -1; // not to be plotted
292  MidExitPos = 0; // not to be plotted
293  MidEntryPos = 0; // not to be plotted
295  LeadExitPos = 1; // will be 1 for continuation entry
296  LeadEntryPos = 0;
297 
299  MaxExitSpeed = StartSpeed; // initial value
301  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
302  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
303  if(EntrySpeed > SpeedLimit)
304  EntrySpeed = SpeedLimit;
308  // LeadElement is the element to be entered
309 
310  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
311  // can achieve ExitSpeedFull at the half braking rate.
313  {
314  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
315  if(TempEntrySpeed < EntrySpeed)
316  {
317  EntrySpeed = TempEntrySpeed;
319  }
320  }
321  Straddle = MidLag; // only for starting on a continuation
323  // no need to stop gap flashing if start on continuation
324  }
325  else // not starting at a continuation
326  {
327  LagElement = -1;
328  LagEntryPos = 0;
329  LagExitPos = 0;
336 
338  MaxExitSpeed = StartSpeed; // initial value
340  bool TempDerail = false; // dummy
341  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
343  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
344  {
345  StoppedWithoutPower = true;
346  }
347  // facing buffers check - ignore starting speed if start facing buffers
348  StoppedAtBuffers = false;
349  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
352  {
353  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
354  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
355  EntrySpeed = 0;
356  ExitSpeedHalf = 0;
357  ExitSpeedFull = 0;
358  MaxExitSpeed = 0;
359  // SetTrainMovementValues not called so set this here
360  BrakeRate = 0;
363  StoppedAtSignal = false;
364  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
365  // signal check is an 'else'
366  if(!StoppedAtLocation)
367  StoppedAtBuffers = true; // stopped at location takes precedence
368  }
369 
370  // facing continuation check - don't allow to stop even if no power
372  {
373  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
374  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
378  BrakeRate = 0;
379  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
380  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
381  }
382 
383  // Signal check
384  else if((NextElementPosition > -1) && (NextEntryPos > -1))
385  // condition check added as precaution after SloughIECC error reported by James U
386  {
387  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
388  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
389  {
390  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
391  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
392  EntrySpeed = 0;
393  ExitSpeedHalf = 0;
394  ExitSpeedFull = 0;
395  MaxExitSpeed = 0;
396  BrakeRate = 0;
399  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
400  {
401  StoppedAtSignal = true;
403  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
404  }
406  { // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
407  // forwards, but don't change the background colour so still shows as stopped at location
408  StoppedAtSignal = true;
409  }
410  }
411  else
412  {
413  StoppedAtSignal = false;
414  if(NextEntryPos > 1)
415  {
416  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
417  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
418  }
419  else
420  {
421  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
422  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
423  }
424  if(EntrySpeed > SpeedLimit)
425  EntrySpeed = SpeedLimit;
429  TDateTime TestTime = TrainController->TTClockTime; // test
430  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
431  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
432  // NextElement is the element to be entered
433 
434  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
435  // can achieve ExitSpeedFull at the half braking rate.
437  {
438  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
439  // half braking
440  if(TempEntrySpeed < EntrySpeed)
441  {
442  EntrySpeed = TempEntrySpeed;
443  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
444  }
445  }
446  }
447  }
449  {
450  throw Exception("Error, LeadElement Exit Connection is NotSet");
451  }
452  }
453 
454  if(MidElement > -1) // will be -1 if start on continuation
455  {
456  Straddle = LeadMid;
460  {
461  for(int x = 0; x < 4; x++)
462  {
463  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
464  }
465  }
466  else
467  {
468  for(int x = 0; x < 4; x++)
469  {
471  }
472  }
473  if(TrainMode == Timetable)
475  else
478  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
479 
482 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
483  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
484  {
485  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
486  Utilities->CallLogPop(651);
487  return false;
488  }
489 */
494  PlotTrainGraphic(8, 0, Display);
495  PlotTrainGraphic(9, 1, Display);
496 
499 
500  // pick up background bitmaps [2] & [3]
501 
504 
505  PlotElement[2] = MidElement;
507  PlotElement[3] = MidElement;
509  PlotTrainGraphic(10, 2, Display);
510  PlotTrainGraphic(11, 3, Display);
511  // Plotted = true; set in PlotTrainGraphic
512  }
513  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
514  Utilities->CallLogPop(652);
515 }
516 
517 // ---------------------------------------------------------------------------
518 void TTrain::UnplotTrain(int Caller)
519 {
520  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
521  if(!Plotted)
522  return;
523 
524  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
525 
526  if(Straddle == MidLag)
527  {
528  if(MidElement > -1)
529  {
534  // to force plot of locked route marker, needed once only for the element
535  }
536  if(LagElement > -1)
537  {
542  // to force plot of locked route marker, needed once only for the element
543  }
544  }
545  else if(Straddle == LeadMidLag)
546  {
547  if(LeadElement > -1)
548  {
551  // to force plot of locked route marker, needed once only for the element
552  }
553  if(MidElement > -1)
554  {
559  // to force plot of locked route marker, needed once only for the element
560  }
561  if(LagElement > -1)
562  {
565  // to force plot of locked route marker, needed once only for the element
566  }
567  }
568  else if(Straddle == LeadMid)
569  {
570  if(LeadElement > -1)
571  {
576  // to force plot of locked route marker, needed once only for the element
577  }
578  if(MidElement > -1)
579  {
584  // to force plot of locked route marker, needed once only for the element
585  }
586  }
587 
588  if(LeadElement > -1)
590  if(MidElement > -1)
592  if(LagElement > -1)
594  Plotted = false;
596  Display->Update();
597  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
598  // resurrected when Update() dropped from PlotOutput etc
599  Utilities->CallLogPop(653);
600 }
601 
602 // ----------------------------------------------------------------------------
603 
604 void TTrain::UpdateTrain(int Caller)
605 /*
606  Note: Some changes made since comments written
607 
608  Brief:
609  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
610  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
611  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
612  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
613  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
614  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
615  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
616  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
617  changed to MidLag within the function and all elements moved down one, old Mid becomes
618  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
619  incremented to reflect the position the train now occupies.
620 
621  Detail:
622  Set TrainFailurePending if all conditions met
623  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
624  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
625  and return.
626  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
627  If there's a LagElement (there will be but include check for good practice - next
628  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
629  train fully on offending point - Derail set and DerailPanding reset, train background
630  colour changed (note that BackgroundColour is a property of the train itself) then return.
631  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
632  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
633  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
634  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
635  if LeadElement is a fouled trailing point.
636  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
637  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
638  replotting the last background segment and checking whether the element is a bridge or crossover with the other
639  track in a route, in which case the route colour is replotted.
640  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
641  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
642  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
643  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
644  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
645  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
646  train can be deleted by the calling function, and the function returns.
647  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
648  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
649  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
650  basic red aspect.
651 
652  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
653  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
654  regardless of direction, and with the correct front code colour.
655 
656  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
657  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
658  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
659  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
660  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
661  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
662  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
663 
664  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
665  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
666  changed similarly. The function then returns.
667 
668  If Crashed is not set then Straddle is incremented and the function returns.
669 */
670 
671 {
672  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
673  UpdateCounter++;
674  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
675  if(UpdateCounter >= 100)
676  UpdateCounter = 0;
677 
678  int RandRange = (TrainController->MTBFHours * 3600) / 53;
679 
680  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
681  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
682  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
683  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
684  // RandomFailureCounter value is fixed for a full cycle of train updates so this
685  // makes sure there's no bunching of failures as there is for a fixed comparison number
686  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
687  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
688  {
689  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
690  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
691  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
692  // don't fail if:
693  // (a) on a continuation (entering or leaving);
694  // (b) already failed;
695  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
696  // (d) train terminated;
697  // (e) crashed or derailed; or
698  // (f) under signaller control and stopped.
699  {
700  if((random(RandRange)) == 0)
701  // max value for RandRange is over 2x10^9
702  { // here if failure due
703  TrainFailurePending = true;
704  // fail when PlotElements set to proper Lead, Mid & Elements
705  }
706  }
707  }
708 
709 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
710  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
711  {
712  StoppedWithoutPower = true;
713  }
714 */
715  int LockedVectorNumber;
716  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
717  // default values - these needed for route checker below
718  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
719 
722 
723  if(Crashed || Derailed)
724  {
726  {
727  PlotTrain(7, Display);
728  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
729  Display->Update();
730  }
731  OpTimeToAct = 0.0;
732  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
733  Utilities->CallLogPop(1017);
734  return; // no further action, user has to remove or work around
735  }
736 
738  {
740  }
741 
743  {
745  }
746 
748  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
749  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
750  // to move & then stop again at the same station
751  {
752  TimeTimeLocArrived = false;
753  }
754 
755  if(!Stopped() && !SPADFlag && !TrainFailed)
756  {
758  }
759 
760  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
761  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
762 /* old version where force a stop at buffers regardless of speed
763  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
764  else StoppedAtBuffers = false;
765 */
766 
767  // new version where crash if run into buffers
768  if(!Crashed)
769  {
770  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
771  {
772  if(ExitSpeedFull > 1)
773  {
774  Crashed = true;
778  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
779  // no need for missed action logs - will be sent when train removed
780  StoppedAtBuffers = false;
781  }
783  // stopped at location & stopped without power take precedence
784  {
785  StoppedAtBuffers = true;
786  }
787  else
788  StoppedAtBuffers = false;
789  }
790  else
791  StoppedAtBuffers = false;
792  }
793  else
794  StoppedAtBuffers = false;
795  // if crashed don't want stopped at buffers set
796 
797  // also crash if run into a level crossing that is changing or has barriers up
798  if(!Crashed)
799  {
800  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
801  {
802  int H = Track->TrackElementAt(873, LeadElement).HLoc;
803  int V = Track->TrackElementAt(874, LeadElement).VLoc;
804  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
805  {
806  Crashed = true;
810  // no need for missed action logs - will be sent when train removed
811  }
812  }
813  }
814 
816  {
818  }
819 
820  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
821  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
822  HoldAtLocationInTTMode = true;
823  else if(TrainMode == Timetable)
824  HoldAtLocationInTTMode = false;
825  // in Signaller mode HoldAtLocationInTTMode not changed
826 
827  // check if departure pending & set times unless already set
828  if(TrainMode == Timetable)
829  {
831  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
832  {
833  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
834  {
836  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
837  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
838  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
839  DepartureTimeSet = true;
840  }
841  }
842  }
843 
845  {
846  OpTimeToAct = CalcTimeToAct(0); // called after ReleaseTime set
847  // calculate every 1 sec (in real time, not timetable time) for all trains
848  }
849 
850  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
851  if(TrainMode == Timetable)
852  {
853  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
854  {
855  RemainHereLogNotSent = true;
856  }
858  {
859  // ignore TimeLoc & TTLoc departures
860  // Action logs given in functions
862  LastActionTime + TDateTime(30.0 / 86400)))
863  {
864  if(ActionVectorEntryPtr->Command == "fsp")
865  { // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
866  FrontTrainSplit(0);
867  if(TrainFailurePending) // ok, stopped so PlotElements set
868  {
869  TrainHasFailed(0);
870  }
871  Utilities->CallLogPop(2041);
872  return;
873  }
874  else if(ActionVectorEntryPtr->Command == "rsp")
875  { // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
876  RearTrainSplit(0);
877  if(TrainFailurePending) // ok, stopped so PlotElements set
878  {
879  TrainHasFailed(1);
880  }
881  Utilities->CallLogPop(2042);
882  return;
883  }
884  else if(ActionVectorEntryPtr->Command == "Fjo")
885  FinishJoin(0);
886  else if(ActionVectorEntryPtr->Command == "jbo")
887  JoinedBy(0);
888  else if(ActionVectorEntryPtr->Command == "cdt")
890  else if(ActionVectorEntryPtr->Command == "Fns")
891  NewTrainService(0);
892  else if(ActionVectorEntryPtr->Command == "Frh")
893  RemainHere(0);
894  else if(ActionVectorEntryPtr->Command == "Fer")
895  TimetableFinished = true;
896  // other aspects of 'Fer' dealt with in TTrain::Update()
897  else if(ActionVectorEntryPtr->Command == "F-nshs")
899  else if(ActionVectorEntryPtr->Command == "Frh-sh")
901  else if(ActionVectorEntryPtr->Command == "Fns-sh")
903 /*
904  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
905  shuttle headcode (no train creation)
906  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
907  remain here
908  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
909  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
910 */
911  }
912  }
913  else
914  {
916  {
918  }
919  }
920  }
921  if(TrainMode == Timetable)
922  {
923  if(StoppedAtBuffers)
924  {
925  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
926  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
927  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
928  if(BufferLocation == "")
930  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
931  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
932  {
936  {
938  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
939  // Drop missed actions so user can still use sig mode to get back on track
941  }
942  if(TrainFailurePending) // ok, stopped so PlotElements set
943  {
945  TrainHasFailed(2);
946  }
947  Utilities->CallLogPop(1020);
948  return;
949  }
950  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
951  ReleaseTime))
952  {
955  {
958  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
959  // Drop missed actions so user can still use sig mode to get back on track
961  }
962  if(TrainFailurePending) // ok, stopped so PlotElements set
963  {
965  TrainHasFailed(3);
966  }
967  Utilities->CallLogPop(1397);
968  return;
969  }
970  }
971  else
972  {
974  }
975  }
976  else
977  {
979  }
980 
981  if(TrainMode == Timetable)
982  {
984  {
986  }
988  {
990  }
991  }
992 
993  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
994  // restart after stopped for train in front
995  int NextElementPosition, NextEntryPos;
996 
997  if(LeadElement > -1) // if an exit continuation then not set
998  {
999  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1000  {
1002  }
1003  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1004  {
1005  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1006  {
1007  LeadExitPos = 1;
1008  }
1009  else
1010  {
1011  LeadExitPos = 3;
1012  }
1013  }
1014  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1015  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1016  }
1017  else
1018  {
1019  NextElementPosition = -1;
1020  NextEntryPos = -1;
1021  }
1022 
1023  if((NextElementPosition > -1) && (NextEntryPos > -1))
1024  // may be buffers or continuation so need this check
1025  {
1026 /*
1027  Check whether calling-on conditions met:-
1028  a) approaching train has stopped at a signal but not at a location;
1029  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1030  change of direction (cdt), remaining here (Frh), or under signaller control);
1031  c) at least 1 platform available for the approaching train;
1032  d) points (if any) set for direct route into platform;
1033  e) approaching train is to stop at station;
1034  f) no more facing signals between train and platform;
1035  g) [dropped g]
1036  h) train in front preventing route being set far enough to release stop signal;
1037  i) train in front not exiting at continuation;
1038  j) signal must be within 4km of the stop platform;
1039  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily]; and
1040  l) no existing route conflicts with the route into the platform.
1041  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1042  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1043 */
1044  if(TrainMode == Timetable)
1045  {
1046  if(CallingOnAllowed(0))
1047  {
1048  CallingOnFlag = true;
1049  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1050  }
1051  else
1052  {
1053  if(CallingOnFlag)
1054  {
1056  }
1057  CallingOnFlag = false;
1058  }
1059  }
1060  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed)
1061  {
1062  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1063  // sets StoppedAtSignal again & train doesn't move
1064  StoppedAtSignal = false;
1065  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1066  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1067  // LeadMidLag and front of train was on LeadElement (after the current move)
1069  EntrySpeed = 0;
1071  FirstHalfMove = true;
1072  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1073  // NextElement is the element to be entered
1074  }
1076  {
1077  if(ClearToNextSignal(0))
1078  {
1079  StoppedForTrainInFront = false;
1080  BeingCalledOn = false;
1081  EntrySpeed = 0;
1083  FirstHalfMove = true;
1084  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1085  }
1086  else
1087  {
1088  if(TrainFailurePending) // ok, stopped so PlotElements set
1089  {
1090  TrainHasFailed(4);
1091  }
1092  Utilities->CallLogPop(1097);
1093  return;
1094  }
1095  }
1096  }
1097 
1098  if((Straddle == MidLag) && (LeadElement != -1))
1099  // later check only for Straddle == LeadMid, so need this check here for initial train start
1100  {
1102  }
1103 
1104 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1105  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1106  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1107  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1108  which could be when start as Snt.
1109  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1110  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1111  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1112  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1113  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1114  reached.
1115  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1116  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1117  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1118  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1119  sending a message to the performancelog.
1120 */
1121 
1122  if(TrainMode == Timetable)
1123  {
1125  {
1126  if(BeingCalledOn)
1127  {
1128  StoppedForTrainInFront = true;
1129  }
1131  {
1133  }
1135  {
1136  // value updated at every scheduled departure & arrival
1138  AnsiString StationName;
1140  {
1142  }
1144  {
1146  }
1147  else
1148  {
1149  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1150  }
1151  EntrySpeed = 0;
1155  FirstHalfMove = true;
1156  StoppedAtLocation = false;
1157 
1158  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1159  {
1160  StoppedWithoutPower = true;
1161  }
1162 
1163  if((NextElementPosition > -1) && (NextEntryPos > -1))
1164  // condition check added for SloughIECC error reported by James U
1165  {
1166  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1167  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1168  {
1169  StoppedAtSignal = true;
1170  if(!StoppedWithoutPower)
1171  // if stopped without power just keep existing background colour
1172  {
1174  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1175  }
1176  }
1177  }
1179  {
1180  TimeTimeLocArrived = false;
1181  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1182  // no warning for TimeTimeLoc departure
1183  }
1184  else
1185  {
1187  }
1188  DepartureTimeSet = false;
1189  // no need to set LastActionTime for a departure
1191  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1192  // note
1193 /*
1194  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1195  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1196  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1197  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1198  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1199  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1200  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1201 */
1203  {
1204  StoppedAtBuffers = true;
1205  }
1206  else if(!StoppedWithoutPower)
1207  // if buffers or no power, don't set values
1208  {
1210  {
1211  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1212  // NextElement is the element to be entered
1213  }
1214  else
1215  {
1217  // use LeadElement for an exit continuation
1218  }
1219  }
1220  }
1221  }
1222  }
1223 
1224  if(Straddle == LeadMidLag)
1225  {
1227  {
1228  Utilities->CallLogPop(654);
1229  return;
1230  }
1231  }
1232  else
1233  {
1235  {
1236  Utilities->CallLogPop(655);
1237  return;
1238  }
1239  }
1240 
1241  if((LeadElement > -1) && (MidElement > -1))
1242  {
1244  { // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1245  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1246  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1247  SignallerStoppingFlag = false;
1248  StepForwardFlag = false;
1249  }
1250  }
1251 
1252  if(Stopped())
1253  // this is what prevents another movement if the train is stopped
1254  {
1255  if(TrainFailurePending) // ok, stopped so PlotElements set
1256  {
1257  TrainHasFailed(5);
1258  }
1259  BrakeRate = 0;
1260  Utilities->CallLogPop(656);
1261  return;
1262  }
1263 
1264  // here when ready for next move
1265 
1266  // check for train in front & if so stop at next access (when train fully on element next to train)
1267  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1268  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1269  // variable TrainInFrontInSignallerModeFlag
1270  {
1271  if(LeadElement > -1)
1272  {
1273  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1274  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1275  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1276  // true if another train on NextEntryPos track whether bridge or not
1277  {
1278  StoppedForTrainInFront = true;
1279  }
1280  else
1281  {
1282  StoppedForTrainInFront = false;
1283  }
1284  }
1285  }
1286 
1287  if((Straddle == LeadMid) && SPADFlag)
1288  // give message + plot background when ready to move half past the signal
1289  {
1290  if(NextElementPosition > -1)
1291  {
1292  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1293  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1294  {
1295  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1297  // if goes past 2 signals then give message twice
1299  }
1300  }
1301  }
1302 
1303  if(Straddle == LeadMidLag)
1304  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1305  {
1306  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1307  if(SPADFlag)
1308  {
1309  if(ExitSpeedFull == 0)
1310  {
1311  StoppedAfterSPAD = true;
1312  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1313  }
1314  }
1315 
1317  {
1318  if(ExitSpeedFull == 0)
1319  {
1320  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1321  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1322  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1323  // is sent at the right time and once only.
1324  SignallerStopped = true;
1325  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1326  StepForwardFlag = false;
1327  SignallerStoppingFlag = false;
1328  TTrackElement TE;
1329  AnsiString Loc = "";
1330  bool LocNamed = false;
1331  if(LeadElement > -1)
1332  {
1333  TE = Track->TrackElementAt(782, LeadElement);
1334  if(TE.ActiveTrackElementName != "")
1335  {
1336  Loc = TE.ActiveTrackElementName;
1337  LocNamed = true;
1338  }
1339  else
1340  {
1341  Loc = "track element " + TE.ElementID;
1342  }
1343  }
1344  if((MidElement > -1) && !LocNamed)
1345  {
1346  TE = Track->TrackElementAt(783, MidElement);
1347  if(TE.ActiveTrackElementName != "")
1348  {
1349  Loc = TE.ActiveTrackElementName;
1350  LocNamed = true;
1351  }
1352  else if(Loc == "")
1353  {
1354  Loc = "track element " + TE.ElementID;
1355  }
1356  }
1357  if(Loc == "")
1358  {
1359  Loc = "outside railway";
1360  // must have stopped after left at a continuation (because both lead & mid == -1)
1361  }
1362  else
1363  Loc = "at " + Loc;
1364  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1365  }
1366  }
1367  if(LeadElement > -1) // if an exit continuation then not set
1368  {
1369  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1370  {
1372  }
1373  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1374  {
1375  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1376  LeadExitPos = 1;
1377  else
1378  LeadExitPos = 3;
1379  }
1380  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1381  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1382  }
1383  else
1384  {
1385  NextElementPosition = -1;
1386  NextEntryPos = -1;
1387  }
1388 
1391  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1392 
1393  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1394  {
1395  StoppedWithoutPower = true;
1396  }
1397 
1398  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1399  // may be buffers or continuation. SPADFlag added at v2.1.0
1400  // so don't override the SPAD colour & don't set StoppedAtSignal
1401  {
1402  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1403  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1404  {
1405  StoppedAtSignal = true;
1406  if(!StoppedWithoutPower)
1407  // leave background as is if no power, but set StoppedAtSignal
1408  {
1410  }
1411  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1412  }
1413  }
1414 
1415  if(!Stopped())
1416  {
1417  if((NextElementPosition > -1) && (NextEntryPos > -1))
1418  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1419  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1420  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1421  // function for fuller explanation
1422  {
1423  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1424  // NextElement is the element to be entered
1425  }
1426  // follow the continuation exits:-
1427  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1428  {
1430  // Use LeadElement for calcs if lead is a continuation
1431  }
1432  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1433  {
1435  // Use MidElement for calcs if mid is a continuation
1436  }
1437  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1438  {
1440  // Use LagElement for calcs if lag is a continuation
1441  }
1442  }
1443 
1444  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1445  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1446  // Trains may not be in a route
1447  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1448  {
1449  // NB if LeadElement == -1 then the above test returns false
1450  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1451  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1452  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1453  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1454  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1455  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1456  FirstPair.second).GetELink() == TempELink))
1457  {
1458  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1459  }
1460  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1461  SecondPair.second).GetELink() == TempELink))
1462  {
1463  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1464  }
1465  }
1466 
1467  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1468  // Trains may not be in a route
1469  {
1470  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1471  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1472  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1473  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1474  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1475  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1476  FirstPair.second).GetELink() == TempELink))
1477  {
1478  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1479  }
1480  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1481  SecondPair.second).GetELink() == TempELink))
1482  {
1483  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1484  }
1485  }
1486 
1487  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1488  // Trains may not be in a route
1489  {
1490  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1491  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1492  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1493  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1494  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1495  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1496  FirstPair.second).GetELink() == TempELink))
1497  {
1498  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1499  }
1500  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1501  SecondPair.second).GetELink() == TempELink))
1502  {
1503  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1504  }
1505 
1506  AllRoutes->CheckMapAndRoutes(8); // test
1507  }
1508 
1509  if(LagElement > -1)
1510  // not entering at a continuation so can deal with train leaving the lag element
1511  {
1513  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1514  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1515  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1516 
1517  TPrefDirElement PrefDirElement;
1518  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1519  // as this is a 16x16 graphic
1521  {
1523  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1524  }
1525 
1526  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1527  {
1528  int RouteNumber;
1529  TrainGone = true;
1530  // flag to indicate train to be deleted - outside this function
1532  {
1533  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1534  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1535  // calc distance from & inc last signal to exit
1536  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1537  int NewLastElement = 0, NewLastExitPos = 0;
1538  // need above because can't change LastElement & LastExitPos until both new values obtained
1539  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1540  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1541  LastElement).TrackType != Points))
1542  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1543  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1544  // leave CumDistance as it was in these circumstances.
1545  {
1546  if(LastExitPos < 2)
1547  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1548  else
1549  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1550  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1551  if(NewLastElement == -1)
1552  // this will catch buffers or any other connection failure
1553  {
1554  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1555  }
1556  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1557  if(NewLastExitPos == -1)
1558  {
1559  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1560  }
1561  LastElement = NewLastElement;
1562  LastExitPos = NewLastExitPos;
1563  }
1564  // if at signal add this in too
1565  if(CumDistance < 1200)
1566  {
1567  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1568  }
1569  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1570  // else use 1200m - CumDistance
1571  int FirstDistance = 0;
1572  if(CumDistance >= 1200)
1573  FirstDistance = 100;
1574  else
1575  FirstDistance = 1200 - CumDistance;
1576  if(FirstDistance < 100)
1577  FirstDistance = 100; // don't allow < 100
1578  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1579  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1580  if(ExitSpeedFull > 20.0)
1581  {
1582  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1583  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1584  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1585  // 4320.0 = 3.6 * 1200, .0 to make it a double
1586  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1587  }
1588  else
1589  {
1590  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1591  ContinuationAutoSigEntry.SecondDelay = 120.0;
1592  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1593  }
1594  ContinuationAutoSigEntry.AccessNumber = 0;
1595  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1597  {
1599  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1600  VectorIT++)
1601  {
1602  if(VectorIT->RouteNumber == RouteNumber)
1603  {
1604  // another train has passed out of same route so erase earlier entry
1605  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1606  break;
1607  }
1608  }
1609  }
1610  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1611  }
1613  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1614  Display->Update();
1615  // need to keep this since Update() not called for PlotSmallOutput as too slow
1616  Utilities->CallLogPop(659);
1617  return;
1618  }
1619  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1620  if(LeadElement > -1)
1621  {
1623  // changed to lead so reset early
1624  {
1625  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1627  // don't plot if zoomed out
1628  if(!Display->ZoomOutFlag)
1630  // covers signal resetting in same direction
1631  }
1632  }
1634  {
1635  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1636  {
1637  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1638  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1639  TPrefDirElement PrefDirElement;
1640  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1642  {
1644  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1645  }
1647  LockedVectorNumber)))
1648  {
1650  }
1651  }
1652  }
1653  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1654  {
1655  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1657  // don't plot if zoomed out
1658  if(!Display->ZoomOutFlag)
1660  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1661  }
1663  {
1664  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1665  {
1666  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1667  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1668  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1669  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1670  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1671  int RouteNumber;
1673  // already know it's an autosigsroute, this is just to get the RouteNumber
1674  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1675  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1676  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1677  int RouteNumber2;
1679  // already know it's an autosigsroute, this is just to get the RouteNumber
1680  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1681  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1682  {
1683  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1684  // this was in the 1.3.0 addition but without the condition
1685  }
1686  // end of 1.3.2 addition
1687  // end of 1.3.0.addition
1688  }
1689  TPrefDirElement PrefDirElement;
1690  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1692  {
1694  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1695  }
1696  }
1697  }
1698  }
1699 
1700  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1701  if(Straddle == LeadMid)
1702  {
1703  AllowedToPassRedSignal = false;
1704  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1705  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1706  if(DerailPending)
1707  // set during last GetLeadElement, but only act on it when train fully on offending point
1708  // i.e. next time Straddle reaches LeadMid
1709  {
1710  Derailed = true;
1711  DerailPending = false;
1715  Utilities->CallLogPop(657);
1716  return;
1717  }
1724  Straddle = MidLag;
1725  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1726  // LeadElement during this function (note that if stopped at signal then won't get this far)
1727  if(LeadElement > -1)
1728  {
1730  // i.e an exit continuation only
1731  // if don't exclude entry continuations then can't progress past it
1732  {
1733  LeadElement = -1;
1734  }
1735  else
1736  {
1737  GetLeadElement(0);
1738  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1740  if(Stopped())
1741  {
1742  if(TrainFailurePending) // ok, stopped so PlotElements set
1743  {
1744  TrainHasFailed(6);
1745  }
1746  Utilities->CallLogPop(658);
1747  return; // i.e. don't move forward one step if next element is a red signal
1748  }
1749  }
1750  }
1751  }
1752 
1753  if(LagElement > -1)
1754  {
1755  // below are the actions required at both half moves for LagElement > -1
1757 
1758  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1759  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1760  // need to do this for each half element
1761 
1762  TPrefDirElement PrefDirElement;
1763  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1764  {
1765  int RouteNumber; // holder for call below - not used
1767  {
1768  if(Utilities->clTransparent == TColor(0xFFFFFF))
1769  // change to black for a white background
1770  {
1772  // only applies for AutoSigs Route in case was locked & timed out
1773  }
1774  else
1775  // change to white for a dark background
1776  {
1778  // only applies for AutoSigs Route in case was locked & timed out
1779  }
1781  }
1782  }
1783 
1785  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1786  // or a train on the opposite track - needs to be replotted
1787  }
1788 
1789  // update all array values
1790  HOffset[3] = HOffset[2];
1791  HOffset[2] = HOffset[1];
1792  HOffset[1] = HOffset[0];
1793  VOffset[3] = VOffset[2];
1794  VOffset[2] = VOffset[1];
1795  VOffset[1] = VOffset[0];
1796  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1797 
1798  BackgroundPtr[3] = BackgroundPtr[2];
1799  BackgroundPtr[2] = BackgroundPtr[1];
1800  BackgroundPtr[1] = BackgroundPtr[0];
1801  BackgroundPtr[0] = TempPtr;
1802 
1803  // update headcode graphics depending on Lead entry value
1804  if(LeadElement > -1) // if Lead is -1 then stays as is
1805  {
1807  {
1808  for(int x = 0; x < 4; x++)
1809  {
1810  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1811  }
1812  }
1813  else
1814  {
1815  for(int x = 0; x < 4; x++)
1816  {
1818  }
1819  }
1820  }
1821 
1822  if(TrainMode == Timetable)
1823  {
1825  }
1826  else
1827  {
1829  }
1831 
1832  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1833  if(LeadElement > -1)
1834  {
1835  if(Straddle == MidLag)
1836  // just about to move half onto the new lead element
1837  {
1839  // pick up new background bitmap [0]
1841  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1842  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1843  // check if own ID for entry at continuation, else crashes into itself!
1844  {
1845  // OK if crossing on a bridge
1846  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1847  if(OtherTrainEntryPos == -1)
1848  {
1849  throw Exception("Error - OtherTrainEntryPos not set");
1850  }
1851  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1852  // LeadEntryPos for rear end crashes
1853  (LeadExitPos == OtherTrainEntryPos))
1854  // LeadExitPos for head-on crashes
1855  {
1857  Crashed = true; // only set if Straddle = MidLag
1858  CallingOnFlag = false;
1859  // in case was set, need to disable call on if call on button had been pressed
1860  }
1861  }
1862  else if(MidElement > -1) // will be -1 for continuation entries
1863  {
1864  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1865  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1866  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1867  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1868  int OtherTrainID = -1;
1869  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1870  {
1871  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1872  {
1873  TrainCrashedInto = OtherTrainID;
1874  Crashed = true; // only set if Straddle = MidLag
1875  CallingOnFlag = false;
1876  // in case was set, need to disable call on if call on button had been pressed
1877  }
1878  }
1879  }
1880  }
1881  else
1882  {
1884  // pick up new background bitmap [0]
1886  }
1887  PlotElement[0] = LeadElement;
1889  PlotTrainGraphic(12, 0, Display);
1890  }
1891 
1892  if(MidElement > -1)
1893  {
1894  PlotElement[2] = MidElement;
1896  PlotTrainGraphic(1, 2, Display);
1897  }
1898 
1899  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
1900  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
1901  if(Straddle == MidLag)
1902  {
1903  if(MidElement > -1)
1904  {
1905  PlotElement[1] = MidElement;
1907  PlotTrainGraphic(2, 1, Display);
1908  }
1909  if(LagElement > -1)
1910  {
1911  PlotElement[3] = LagElement;
1913  PlotTrainGraphic(3, 3, Display);
1914  }
1915  }
1916  else // Straddle == LeadMidLag
1917  {
1918  if(LeadElement > -1)
1919  {
1920  PlotElement[1] = LeadElement;
1922  PlotTrainGraphic(4, 1, Display);
1923  }
1924  if(MidElement > -1)
1925  {
1926  PlotElement[3] = MidElement;
1928  PlotTrainGraphic(5, 3, Display);
1929  }
1930  }
1931 
1932  if(Crashed)
1933  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
1934  {
1939  // in case was set, need to disable call on if call on button had been pressed
1946  Straddle = LeadMidLag;
1947  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
1948  Display->Update();
1949  // resurrected when Update() dropped from PlotOutput etc
1950  Utilities->CallLogPop(660);
1951  return;
1952  }
1953 
1954  // deal here with station stops & pass times after all replotting done but before Straddle changed
1955  if(TrainMode == Timetable)
1956  {
1957  if(Straddle == LeadMidLag)
1958  {
1959  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
1960  {
1961  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
1962  // to point to the location arrival entry - before a change of direction
1963  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
1964  bool StopRequired = false;
1965  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
1966  if(TTVPos > -1) // -1 if can't find it or if name is ""
1967  {
1968  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
1969  // or a station where next element contains a train or a stop signal, if so
1970  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
1971  // to test the actual track the train is on since it can't be a platform
1972  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
1973  TTrackElement NextTrackElement; // default for now
1974  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
1975  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
1976  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
1977  int NextElementEntryPos = -1;
1978  int NextElementExitPos = -1;
1979  bool TrainOnNextElement = false;
1980  bool StopSignalAtNextElement = false;
1981  if(ForwardConnection)
1982  // if no forward connection can't derive anything from it without errors
1983  {
1984  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
1985  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
1986  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
1987  // this is only for signals so no need to worry about points ambiguity
1988  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
1989  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
1990  }
1991  // logic here is: if(train@stoplinkpos1 || train@stoplinkpos2 || (forward connection && (train on next element || stop signal at next element)))
1992  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
1993  {
1994  if(TTVPos > 0)
1995  {
1997  ActionVectorEntryPtr += TTVPos;
1998  }
1999  if(StopRequired)
2000  {
2001  StoppedAtLocation = true;
2002  StoppedAtSignal = false;
2003  // may have been set earlier at line 925 so need to reset as
2004  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2005  // in zoom out mode
2006  if(!TrainFailed)
2007  {
2009  // pale green
2010  }
2013  {
2014  TimeTimeLocArrived = true;
2015  // used in case of later signaller control, when need to know
2016  // whether had arrived or not, to avoid sending the arrival
2017  // message twice, see TInterface::TimetableControl1Click
2018  }
2019  }
2020  else
2021  {
2023  }
2025  {
2027  }
2028  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2030  }
2031  }
2032  }
2033  }
2034  }
2035 
2036  if(Straddle == MidLag)
2037  {
2038  Straddle = LeadMidLag;
2039  FirstHalfMove = false;
2040  }
2041  else if(Straddle == LeadMidLag)
2042  {
2043  Straddle = LeadMid;
2044  FirstHalfMove = true;
2045  }
2046  else if(Straddle == LeadMid)
2047  {
2048  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2049  }
2050 
2051  if(TrainFailurePending) // ok, moving but PlotElements set above
2052  {
2053  TrainHasFailed(7);
2054  }
2055  Display->Update();
2056  // need to keep this since Update() not called for PlotSmallOutput as too slow
2057  Utilities->CallLogPop(661);
2058 }
2059 
2060 // ----------------------------------------------------------------------------
2061 
2062 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2063 {
2064  switch(CodeChar)
2065  {
2066  case '0':
2067  return RailGraphics->Code0;
2068 
2069  case '1':
2070  return RailGraphics->Code1;
2071 
2072  case '2':
2073  return RailGraphics->Code2;
2074 
2075  case '3':
2076  return RailGraphics->Code3;
2077 
2078  case '4':
2079  return RailGraphics->Code4;
2080 
2081  case '5':
2082  return RailGraphics->Code5;
2083 
2084  case '6':
2085  return RailGraphics->Code6;
2086 
2087  case '7':
2088  return RailGraphics->Code7;
2089 
2090  case '8':
2091  return RailGraphics->Code8;
2092 
2093  case '9':
2094  return RailGraphics->Code9;
2095 
2096  case 'A':
2097  return RailGraphics->CodeA;
2098 
2099  case 'B':
2100  return RailGraphics->CodeB;
2101 
2102  case 'C':
2103  return RailGraphics->CodeC;
2104 
2105  case 'D':
2106  return RailGraphics->CodeD;
2107 
2108  case 'E':
2109  return RailGraphics->CodeE;
2110 
2111  case 'F':
2112  return RailGraphics->CodeF;
2113 
2114  case 'G':
2115  return RailGraphics->CodeG;
2116 
2117  case 'H':
2118  return RailGraphics->CodeH;
2119 
2120  case 'I':
2121  return RailGraphics->CodeI;
2122 
2123  case 'J':
2124  return RailGraphics->CodeJ;
2125 
2126  case 'K':
2127  return RailGraphics->CodeK;
2128 
2129  case 'L':
2130  return RailGraphics->CodeL;
2131 
2132  case 'M':
2133  return RailGraphics->CodeM;
2134 
2135  case 'N':
2136  return RailGraphics->CodeN;
2137 
2138  case 'O':
2139  return RailGraphics->CodeO;
2140 
2141  case 'P':
2142  return RailGraphics->CodeP;
2143 
2144  case 'Q':
2145  return RailGraphics->CodeQ;
2146 
2147  case 'R':
2148  return RailGraphics->CodeR;
2149 
2150  case 'S':
2151  return RailGraphics->CodeS;
2152 
2153  case 'T':
2154  return RailGraphics->CodeT;
2155 
2156  case 'U':
2157  return RailGraphics->CodeU;
2158 
2159  case 'V':
2160  return RailGraphics->CodeV;
2161 
2162  case 'W':
2163  return RailGraphics->CodeW;
2164 
2165  case 'X':
2166  return RailGraphics->CodeX;
2167 
2168  case 'Y':
2169  return RailGraphics->CodeY;
2170 
2171  case 'Z':
2172  return RailGraphics->CodeZ;
2173 
2174  case 'a':
2175  return RailGraphics->Code_a;
2176 
2177  case 'b':
2178  return RailGraphics->Code_b;
2179 
2180  case 'c':
2181  return RailGraphics->Code_c;
2182 
2183  case 'd':
2184  return RailGraphics->Code_d;
2185 
2186  case 'e':
2187  return RailGraphics->Code_e;
2188 
2189  case 'f':
2190  return RailGraphics->Code_f;
2191 
2192  case 'g':
2193  return RailGraphics->Code_g;
2194 
2195  case 'h':
2196  return RailGraphics->Code_h;
2197 
2198  case 'i':
2199  return RailGraphics->Code_i;
2200 
2201  case 'j':
2202  return RailGraphics->Code_j;
2203 
2204  case 'k':
2205  return RailGraphics->Code_k;
2206 
2207  case 'l':
2208  return RailGraphics->Code_l;
2209 
2210  case 'm':
2211  return RailGraphics->Code_m;
2212 
2213  case 'n':
2214  return RailGraphics->Code_n;
2215 
2216  case 'o':
2217  return RailGraphics->Code_o;
2218 
2219  case 'p':
2220  return RailGraphics->Code_p;
2221 
2222  case 'q':
2223  return RailGraphics->Code_q;
2224 
2225  case 'r':
2226  return RailGraphics->Code_r;
2227 
2228  case 's':
2229  return RailGraphics->Code_s;
2230 
2231  case 't':
2232  return RailGraphics->Code_t;
2233 
2234  case 'u':
2235  return RailGraphics->Code_u;
2236 
2237  case 'v':
2238  return RailGraphics->Code_v;
2239 
2240  case 'w':
2241  return RailGraphics->Code_w;
2242 
2243  case 'x':
2244  return RailGraphics->Code_x;
2245 
2246  case 'y':
2247  return RailGraphics->Code_y;
2248 
2249  case 'z':
2250  return RailGraphics->Code_z;
2251 
2252  default:
2253  return RailGraphics->TempHeadCode;
2254  }
2255 }
2256 
2257 // ----------------------------------------------------------------------------
2258 
2259 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2260 {
2261  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2262  if(Code.Length() != 4)
2263  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2264  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2265  {
2266  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2267  }
2268  if(BackgroundColour != clB5G5R5)
2269  // i.e. not the basic graphic colour as loaded from resource file
2270  {
2271  for(int x = 0; x < 4; x++)
2272  {
2274  }
2275  }
2276  Utilities->CallLogPop(1484);
2277 }
2278 
2279 // ----------------------------------------------------------------------------
2280 
2281 void TTrain::GetLeadElement(int Caller)
2282  // assumes Mid & Lag already set, sets LeadElement,
2283  // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2284 {
2285  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2286  DerailPending = false;
2290  {
2291  // attr 0=straight, - links 0 & 1 (0 = lead)
2292  // attr 1=diverging, - links 2 & 3 (2 = lead)
2293  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2294  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2295 
2296  // if enter at lead, exit at whatever attr set at
2297  // if enter at lag, exit at lead, but set derail wrt attribute
2298  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2299  LeadExitPos = 1;
2300 
2301  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2302  // best to be on safe side
2303  else if(LeadEntryPos == 0)
2304  {
2305  LeadEntryPos = 2;
2306  LeadExitPos = 3;
2307  }
2308  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2309  {
2310  LeadEntryPos = 0;
2311  LeadExitPos = 1;
2312  }
2313  else if(LeadEntryPos == 2)
2314  LeadExitPos = 3;
2315 
2316  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2317  LeadExitPos = 0;
2318  else if(LeadEntryPos == 1)
2319  {
2320  LeadExitPos = 0;
2321  DerailPending = true;
2322  }
2323  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2324  {
2325  LeadExitPos = 0;
2326  DerailPending = true;
2327  }
2328  else if(LeadEntryPos == 3)
2329  LeadExitPos = 0;
2330  }
2331  else if(LeadEntryPos == 0)
2332  LeadExitPos = 1;
2333  else if(LeadEntryPos == 1)
2334  LeadExitPos = 0;
2335  else if(LeadEntryPos == 2)
2336  LeadExitPos = 3;
2337  else if(LeadEntryPos == 3)
2338  LeadExitPos = 2;
2339  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2340 /* signal check moved to Update() function
2341  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2342  && (TrackElement.Attribute == 0))//0 = red
2343  {
2344  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2345  }
2346  else
2347  {
2348  StoppedAtSignal = false;
2349  }
2350 */
2351  Utilities->CallLogPop(662);
2352 }
2353 
2354 // ----------------------------------------------------------------------------
2355 
2356 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2357 {
2358  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2359  switch(Link)
2360  {
2361  case 1:
2362  {
2363  HOffset = 0;
2364  VOffset = 0;
2365  break;
2366  }
2367 
2368  case 2:
2369  {
2370  HOffset = 4;
2371  VOffset = 0;
2372  break;
2373  }
2374 
2375  case 3:
2376  {
2377  HOffset = 8;
2378  VOffset = 0;
2379  break;
2380  }
2381 
2382  case 4:
2383  {
2384  HOffset = 0;
2385  VOffset = 4;
2386  break;
2387  }
2388 
2389  case 6:
2390  {
2391  HOffset = 8;
2392  VOffset = 4;
2393  break;
2394  }
2395 
2396  case 7:
2397  {
2398  HOffset = 0;
2399  VOffset = 8;
2400  break;
2401  }
2402 
2403  case 8:
2404  {
2405  HOffset = 4;
2406  VOffset = 8;
2407  break;
2408  }
2409 
2410  case 9:
2411  {
2412  HOffset = 8;
2413  VOffset = 8;
2414  break;
2415  }
2416 
2417  default:
2418  {
2419  throw Exception("Error in GetOffsetValues - Link value wrong");
2420  }}
2421  Utilities->CallLogPop(674);
2422 }
2423 
2424 // ---------------------------------------------------------------------------
2425 
2426 bool TTrain::LowEntryValue(int EntryLink) const
2427 {
2428 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2429  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2430 */
2431  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2432  return true;
2433  else
2434  return false;
2435 }
2436 
2437 // ---------------------------------------------------------------------------
2438 
2439 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2440 {
2441  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2442  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2443  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2444  // default values
2445  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2446 
2447  TAllRoutes::TRouteType RouteType;
2448 
2449  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2450 
2451  TRect SourceRect, DestRect;
2452 
2453  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2454  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2455  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2456  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2457 
2458  TempGraphic->PixelFormat = pf8bit;
2459  TempGraphic->Width = 16;
2460  TempGraphic->Height = 16;
2461  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2462 
2463  if(TempElement.TrackType == Points)
2464  {
2465  TempGraphic->Assign(TempElement.GraphicPtr);
2466  TempGraphic->Transparent = true;
2467  TempGraphic->TransparentColor = Utilities->clTransparent;
2468  if(RouteType == TAllRoutes::AutoSigsRoute)
2469  {
2470  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2471  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2472  }
2473  else
2474  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2475  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2476  }
2477  else if(TempElement.TrackType == GapJump) // plot set gap
2478  {
2479  if(TempElement.SpeedTag == 88)
2480  TempGraphic->Assign(RailGraphics->gl88set);
2481  else if(TempElement.SpeedTag == 89)
2482  TempGraphic->Assign(RailGraphics->gl89set);
2483  else if(TempElement.SpeedTag == 90)
2484  TempGraphic->Assign(RailGraphics->gl90set);
2485  else if(TempElement.SpeedTag == 91)
2486  TempGraphic->Assign(RailGraphics->gl91set);
2487  else if(TempElement.SpeedTag == 92)
2488  TempGraphic->Assign(RailGraphics->gl92set);
2489  else if(TempElement.SpeedTag == 93)
2490  TempGraphic->Assign(RailGraphics->bm93set);
2491  else if(TempElement.SpeedTag == 94)
2492  TempGraphic->Assign(RailGraphics->bm94set);
2493  else if(TempElement.SpeedTag == 95)
2494  TempGraphic->Assign(RailGraphics->gl95set);
2495  TempGraphic->Transparent = true;
2496  TempGraphic->TransparentColor = Utilities->clTransparent;
2497  if(RouteType == TAllRoutes::AutoSigsRoute)
2498  {
2499  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2500  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2501  }
2502  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2503  }
2504  // new for version 0.6
2505  else if(TempElement.TrackType == SignalPost)
2506  {
2507  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2508  {
2509  for(int x = 0; x < 40; x++)
2510  {
2511  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2512  // need to stop aspect
2513  {
2514  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2515  break;
2516  }
2517  }
2518  }
2519  else // normal signal
2520  {
2521  TempGraphic->Assign(TempElement.GraphicPtr);
2522  // GraphicPtr set to normal signal in a signal track element
2523  }
2524  TempGraphic->Transparent = true;
2525  TempGraphic->TransparentColor = Utilities->clTransparent;
2526  if(RouteType == TAllRoutes::AutoSigsRoute)
2527  {
2528  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2529  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2530  }
2531  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2532  }
2533  else
2534  {
2535  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2536  // can't name points gaps or signals so 'else' OK
2537  bool FoundFlag;
2538  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2539  if(FoundFlag)
2540  {
2542  {
2543  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2544  TempGraphic->Assign(RailGraphics->bmName);
2545  TempGraphic->Transparent = true;
2546  TempGraphic->TransparentColor = Utilities->clTransparent;
2547  if(RouteType == TAllRoutes::AutoSigsRoute)
2548  {
2549  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2550  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2551  }
2552  else
2553  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2554  // draw track on top
2555  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2556  }
2557  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2558  {
2559  TempGraphic->Assign(TempElement.GraphicPtr);
2560  TempGraphic->Transparent = true;
2561  TempGraphic->TransparentColor = Utilities->clTransparent;
2562  // note that can't be an AutoSigsRoute
2563  // now overlay the LC central portion
2564  int BDVectorPos = -1; //not used
2565  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2566  {
2567  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2568  }
2569  else
2570  {
2571  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2572  }
2573  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2574  }
2575  else
2576  {
2577  TempGraphic->Assign(TempElement.GraphicPtr);
2578  TempGraphic->Transparent = true;
2579  TempGraphic->TransparentColor = Utilities->clTransparent;
2580  if(RouteType == TAllRoutes::AutoSigsRoute)
2581  {
2582  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2583  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2584  }
2585  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2586  }
2587  }
2588  else
2589  {
2590  TempGraphic->Assign(TempElement.GraphicPtr);
2591  TempGraphic->Transparent = true;
2592  TempGraphic->TransparentColor = Utilities->clTransparent;
2593  if(RouteType == TAllRoutes::AutoSigsRoute)
2594  {
2595  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2596  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2597  }
2598  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2599  }
2600  }
2601  delete TempGraphic;
2602  Utilities->CallLogPop(675);
2603 }
2604 
2605 // ---------------------------------------------------------------------------
2606 
2607 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2608 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2609 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2610 /*
2611 
2612  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2613  {
2614  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2615  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2616  TAllRoutes::TRouteType RouteType;
2617  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2618  // default values
2619  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2620  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2621  TRect SourceRect, DestRect, ScreenSourceRect;
2622  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2623 
2624  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2625  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2626  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2627 
2628  //add text & user graphics if any to *GraphicPtr prior to adding the track
2629  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2630  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2631  int Right = Left + 8;
2632  int Bottom = Top + 8;
2633  ScreenSourceRect.init(Left, Top, Right, Bottom);
2634  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2635  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2636 
2637  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2638  TempGraphic->PixelFormat = pf8bit;
2639  TempGraphic->Width = 16;
2640  TempGraphic->Height = 16;
2641 
2642  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2643  SourceGraphic->PixelFormat = pf8bit;
2644  SourceGraphic->Width = 16;
2645  SourceGraphic->Height = 16;
2646  SourceGraphic->Transparent = true;
2647  SourceGraphic->TransparentColor = Utilities->clTransparent;
2648 
2649  if (TempElement.TrackType == Points)
2650  {
2651  TempGraphic->Assign(TempElement.GraphicPtr);
2652  TempGraphic->Transparent = true;
2653  TempGraphic->TransparentColor = Utilities->clTransparent;
2654  if (RouteType == TAllRoutes::AutoSigsRoute)
2655  {
2656  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2657  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2658  }
2659  else
2660  {
2661  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2662  }
2663  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2664  }
2665  else if (TempElement.TrackType == GapJump) // plot set gap
2666  {
2667  if (TempElement.SpeedTag == 88)
2668  TempGraphic->Assign(RailGraphics->gl88set);
2669  else if (TempElement.SpeedTag == 89)
2670  TempGraphic->Assign(RailGraphics->gl89set);
2671  else if (TempElement.SpeedTag == 90)
2672  TempGraphic->Assign(RailGraphics->gl90set);
2673  else if (TempElement.SpeedTag == 91)
2674  TempGraphic->Assign(RailGraphics->gl91set);
2675  else if (TempElement.SpeedTag == 92)
2676  TempGraphic->Assign(RailGraphics->gl92set);
2677  else if (TempElement.SpeedTag == 93)
2678  TempGraphic->Assign(RailGraphics->bm93set);
2679  else if (TempElement.SpeedTag == 94)
2680  TempGraphic->Assign(RailGraphics->bm94set);
2681  else if (TempElement.SpeedTag == 95)
2682  TempGraphic->Assign(RailGraphics->gl95set);
2683  TempGraphic->Transparent = true;
2684  TempGraphic->TransparentColor = Utilities->clTransparent;
2685  if (RouteType == TAllRoutes::AutoSigsRoute) {
2686  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2687  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2688  }
2689  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2690  }
2691  // new for version 0.6
2692  else if (TempElement.TrackType == SignalPost)
2693  {
2694  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2695  {
2696  for (int x = 0; x < 40; x++)
2697  {
2698  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2699  // need to stop aspect
2700  {
2701  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2702  break;
2703  }
2704  }
2705  }
2706  else // normal signal
2707  {
2708  TempGraphic->Assign(TempElement.GraphicPtr);
2709  // GraphicPtr set to normal signal in a signal track element
2710  }
2711  TempGraphic->Transparent = true;
2712  TempGraphic->TransparentColor = Utilities->clTransparent;
2713  if (RouteType == TAllRoutes::AutoSigsRoute) {
2714  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2715  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2716  }
2717  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2718  }
2719  else {
2720  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2721  // can't name points gaps or signals so 'else' OK
2722  bool FoundFlag;
2723  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2724  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2725  if (FoundFlag)
2726  {
2727  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2728  {
2729  GraphicPtr->Canvas->CopyRect(DestRect,
2730  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2731  TempGraphic->Assign(RailGraphics->bmName);
2732  TempGraphic->Transparent = true;
2733  TempGraphic->TransparentColor = Utilities->clTransparent;
2734  if (RouteType == TAllRoutes::AutoSigsRoute)
2735  {
2736  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2737  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2738  }
2739  else
2740  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2741  // draw track on top
2742  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2743  SourceRect);
2744  }
2745  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2746  TempGraphic->Assign(TempElement.GraphicPtr);
2747  TempGraphic->Transparent = true;
2748  TempGraphic->TransparentColor = Utilities->clTransparent;
2749  // note that can't be an AutoSigsRoute
2750  // now overlay the LC central portion
2751  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2752  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2753  SourceRect);
2754  }
2755  else {
2756  TempGraphic->Assign(TempElement.GraphicPtr);
2757  TempGraphic->Transparent = true;
2758  TempGraphic->TransparentColor = Utilities->clTransparent;
2759  if (RouteType == TAllRoutes::AutoSigsRoute) {
2760  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2761  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2762  }
2763  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2764  SourceRect);
2765  }
2766  }
2767  else {
2768  TempGraphic->Assign(TempElement.GraphicPtr);
2769  TempGraphic->Transparent = true;
2770  TempGraphic->TransparentColor = Utilities->clTransparent;
2771  if (RouteType == TAllRoutes::AutoSigsRoute) {
2772  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2773  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2774  }
2775  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2776  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2777  }
2778  }
2779  delete TempGraphic;
2780  delete SourceGraphic;
2781  Utilities->CallLogPop();
2782  }
2783 */
2784 // ---------------------------------------------------------------------------
2785 
2786 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2787 {
2788  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2789  if(PlotElement[ArrayNumber] == -1)
2790  {
2791  Utilities->CallLogPop(676);
2792  return; // not plotted yet
2793  }
2794  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2795  // set before plot so gap flashing stops first
2796  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2797  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2798  // Only need to set ID for leading element, stays set until train finally leaves the element
2799  Plotted = true;
2800  Utilities->CallLogPop(677);
2801 }
2802 
2803 // ---------------------------------------------------------------------------
2804 
2805 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2806 {
2807  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2808  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2809 }
2810 
2811 // ---------------------------------------------------------------------------
2812 
2813 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2814 {
2815  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2816  HeadCode);
2817  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2818  {
2819  Utilities->CallLogPop(678);
2820  return true;
2821  }
2822  else
2823  {
2824  Utilities->CallLogPop(679);
2825  return false;
2826  }
2827 }
2828 
2829 // ---------------------------------------------------------------------------
2830 
2831 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2832 {
2833  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2834  "," + HeadCode);
2835  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2836  {
2837  Utilities->CallLogPop(680);
2838  return true;
2839  }
2840  else
2841  {
2842  Utilities->CallLogPop(681);
2843  return false;
2844  }
2845 }
2846 
2847 // ---------------------------------------------------------------------------
2848 
2849 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2850  // test whether this train on a bridge on trackpos 0 & 1
2851 {
2852  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2853  HeadCode);
2854  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2855  {
2856  Utilities->CallLogPop(682);
2857  return false;
2858  }
2859  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2860  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
2861  {
2862  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2863  {
2864  throw Exception("Error, same train on two different bridge tracks");
2865  }
2866  else
2867  {
2868  Utilities->CallLogPop(684);
2869  return true;
2870  }
2871  }
2872  else
2873  {
2874  Utilities->CallLogPop(685);
2875  return false;
2876  }
2877 }
2878 
2879 // ---------------------------------------------------------------------------
2880 
2881 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
2882  // test whether this train on a bridge on trackpos 2 & 3
2883 {
2884  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
2885  HeadCode);
2886  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
2887  {
2888  Utilities->CallLogPop(686);
2889  return false;
2890  }
2891  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2892  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2893  {
2894  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
2895  Utilities->CallLogPop(687);
2896  return true;
2897  }
2898  else
2899  {
2900  Utilities->CallLogPop(688);
2901  return false;
2902  }
2903 }
2904 
2905 // ---------------------------------------------------------------------------
2906 
2907 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2908 {
2909  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2910  AnsiString(EntryPos) + "," + HeadCode);
2911  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
2912 
2913  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
2914  if(Track->GapFlashFlag)
2915  {
2917  {
2920  Track->GapFlashFlag = false;
2921  }
2922  }
2923 
2924  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
2925  {
2926  if(EntryPos == -1)
2927  {
2928  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
2929  }
2930  if(EntryPos < 2)
2931  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
2932  else
2933  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
2934  }
2935  Utilities->CallLogPop(690);
2936 }
2937 
2938 // ---------------------------------------------------------------------------
2939 
2940 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2941 {
2942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2943  AnsiString(EntryPos) + "," + HeadCode);
2944  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
2945  {
2946  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
2947  }
2948  else
2949  {
2950  if(EntryPos == -1)
2951  {
2952  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
2953  }
2954  if(EntryPos < 2)
2955  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
2956  else
2957  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
2958  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
2959  // i.e. other train on track 2&3
2960  {
2961  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
2962  }
2963  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
2964  // i.e. other train on track 1&2
2965  {
2966  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
2967  }
2968  else
2969  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
2970  }
2971  Utilities->CallLogPop(691);
2972 }
2973 
2974 // ---------------------------------------------------------------------------
2975 
2976 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
2977 {
2978  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
2979  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
2980  int LockedVectorNumber;
2981 
2982  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
2983  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
2984  { // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
2985  Utilities->CallLogPop(692);
2986  return;
2987  }
2988  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
2989  // i.e other track is in a marked route
2990  // LinkPos doesn't have to be the entry position for the above check
2991  {
2992  TRect SourceRect, DestRect;
2993  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
2994  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2995  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2996  // identify the route element for the other track
2997  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
2998  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
2999  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3000  int FirstELink, SecondELink = -1;
3001  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3002  // must be at least one
3003  if(RoutePair2.first > -1)
3004  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3005  TPrefDirElement RouteElement;
3006  // Graphics::TBitmap *RouteGraphic;
3007  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3008  // i.e. other track is in RoutePair2
3009  {
3010  if(SecondELink == -1)
3011  {
3012  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3013  }
3014  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3015  // error if both have same Link number
3016  {
3017  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3018  }
3019  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3020  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3021  }
3022  else // other track is in RoutePair1
3023  {
3024  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3025  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3026  }
3027  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3028  DestGraphic->PixelFormat = pf8bit;
3029  DestGraphic->Width = 8;
3030  DestGraphic->Height = 8;
3031  DestGraphic->Transparent = true;
3032  // has to be transparent or will overwrite the track that the train has just left
3033  DestGraphic->TransparentColor = Utilities->clTransparent;
3034  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3035  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3036  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3037  // plot locked route marker for other route if appropriate
3038  TPrefDirElement PrefDirElement; // holder for next call, unused
3039  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3040  if(StraddleValue == LeadMidLag)
3041  {
3043  PrefDirElement, LockedVectorNumber))
3044  {
3045  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3046  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3047  }
3048  }
3049  delete DestGraphic;
3050  }
3051  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3052  // also can only be a bridge or trains either have already or soon will crash
3053  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3054  {
3055  Utilities->CallLogPop(695);
3056  return;
3057  }
3058  if(ElementEntryPos > 1) // other train is on track 01
3059  {
3060  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3061  {
3063  }
3064  }
3065  else // other train is on track 23
3066  {
3067  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3068  {
3070  }
3071  }
3072  Utilities->CallLogPop(696);
3073 }
3074 
3075 // ---------------------------------------------------------------------------
3076 
3077 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3078 {
3079  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3080  AnsiString(EntryPos) + "," + HeadCode);
3081  int RouteNumber;
3082  bool WrongRoute = false;
3083  TPrefDirElement RouteElement;
3085  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3086 
3087  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3088  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3089  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3090  {
3091  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3092  {
3093  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3094  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3095  {
3096  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3097  { // don't call for stub end routes
3099  }
3100  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3101  Utilities->CallLogPop(697);
3102  return;
3103  }
3104  }
3105  // also need to check for a route on a crossing diagonal
3106  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3107  int LinkNumber = TrackElement.Link[EntryPos];
3108  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3109  {
3110  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3111  {
3112  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3113  bool LogActionErrorCalled = false;
3114  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3115  if(LinkNumber == 1)
3116  {
3117  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3118  {
3119  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3120  { // don't call for stub end routes
3122  LogActionErrorCalled = true;
3123  }
3124  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3125  }
3126  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3127  // not else in case have different routes on each diagonal, though shouldn't be possible
3128  {
3129  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3130  { // don't call for stub end routes
3132  }
3133  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3134  }
3135  }
3136 
3137  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3138  else if(LinkNumber == 3)
3139  {
3140  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3141  {
3142  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3143  { // don't call for stub end routes
3145  LogActionErrorCalled = true;
3146  }
3147  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3148  }
3149  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3150  // not else in case have different routes on each diagonal, though shouldn't be possible
3151  {
3152  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3153  { // don't call for stub end routes
3155  }
3156  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3157  }
3158  }
3159 
3160  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3161  else if(LinkNumber == 7)
3162  {
3163  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3164  {
3165  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3166  { // don't call for stub end routes
3168  LogActionErrorCalled = true;
3169  }
3170  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3171  }
3172  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3173  // not else in case have different routes on each diagonal, though shouldn't be possible
3174  {
3175  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3176  { // don't call for stub end routes
3178  }
3179  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3180  }
3181  }
3182 
3183  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3184  else if(LinkNumber == 9)
3185  {
3186  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3187  {
3188  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3189  { // don't call for stub end routes
3191  LogActionErrorCalled = true;
3192  }
3193  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3194  }
3195  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3196  // not else in case have different routes on each diagonal, though shouldn't be possible
3197  {
3198  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3199  { // don't call for stub end routes
3201  }
3202  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3203  }
3204  }
3205  }
3206  }
3207  Utilities->CallLogPop(698);
3208  return; // no route on other track or no other track
3209  }
3210  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3211  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3212  {
3213  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3214  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new after v2.4.1 for points check - Xeon repoted it 30/05/20. He found that for routes that
3215  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3216  {
3217  if(RouteElement.GetELinkPos() == EntryPos)
3218  {
3219  Utilities->CallLogPop(699);
3220  return; // right direction
3221  }
3222  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3223  {
3224  Utilities->CallLogPop(700);
3225  return; // right direction (points)
3226  }
3227  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3228  {
3229  Utilities->CallLogPop(701);
3230  return; // right direction (points)
3231  }
3232  else if(RouteElement.GetXLinkPos() == EntryPos)
3233  {
3234  WrongRoute = true;
3235  break; // wrong direction
3236  }
3237  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3238  {
3239  WrongRoute = true;
3240  break; // wrong direction
3241  }
3242  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3243  {
3244  WrongRoute = true;
3245  break; // wrong direction
3246  }
3247  }
3248  }
3249  if(!WrongRoute)
3250  {
3251  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3252  }
3253  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3254  { // don't call for stub end routes
3256  }
3257  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3258  Utilities->CallLogPop(703);
3259 }
3260 
3261 // ---------------------------------------------------------------------------
3262 
3263 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3264 {
3265  if(BackgroundColour == NewBackgroundColour)
3266  return; // don't replot if already correct
3267 
3268  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3269  bool ColourError = false, ColourError2 = false;
3270 
3271  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3272  if(ColourError)
3273  ColourError2 = true;
3274  for(int x = 0; x < 4; x++)
3275  {
3276  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3277  if(ColourError)
3278  ColourError2 = true;
3279  }
3280  if(ColourError2)
3281  {
3283  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3284  }
3285 
3286  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3287  // of motion
3288  for(int x = 0; x < 4; x++)
3289  {
3290  PlotTrainGraphic(6, x, Disp);
3291  }
3292  BackgroundColour = NewBackgroundColour;
3293  Display->Update();
3294  // need to keep this since Update() not called for PlotSmallOutput as too slow
3295  Utilities->CallLogPop(704);
3296 }
3297 
3298 // ---------------------------------------------------------------------------
3299 
3300 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3301 /*
3302 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3303 
3304 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3305 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3306 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3307 full-element moves.
3308 
3309 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3310 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3311 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3312 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3313 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3314 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3315 In this case set the brake rate to maximum to stop as soon as possible.
3316 
3317 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3318 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3319 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3320 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3321 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3322 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3323 
3324 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3325 first to see whether buffers or continuation) in turn is examined: first the length of the
3326 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3327 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3328 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3329 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3330 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3331 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3332 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3333 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3334 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3335 siding then again emeregency braking may be necessary and a crash may result.
3336 
3337 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3338 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3339 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3340 buffer, then the train accelerates for half the element and brakes for the other half.
3341 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3342 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3343 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3344 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3345 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3346 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3347 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3348 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3349 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3350 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3351 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3352 
3353 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3354 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3355 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3356 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3357 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3358 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3359 MaxBrakeRate.
3360 
3361 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3362 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3363 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3364 
3365 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3366 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3367 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3368 
3369 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3370 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3371 when Straddle == LeadMidLag
3372 */
3373 {
3374  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3375  AnsiString(EntryPos) + "," + HeadCode);
3376  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3377  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3378  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3379  TrainInFrontInSignallerModeFlag = false;
3380  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3381  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3382  bool SignallerStopRequired = false;
3383 
3385  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3386 
3387  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3388 
3389  OneLengthAccelDecel = false;
3390  BrakeRate = 0;
3391 
3392 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3393  if(CurrentTrackVectorPosition > -1)
3394  {
3395  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3396  {
3397  if((EntryPos == 0) || (EntryPos == 2))
3398  {
3399  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3400  {
3401  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3402  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3403  }
3404  else
3405  {
3406  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3407  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3408  }
3409  }
3410  else if(EntryPos == 1)
3411  {
3412  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3413  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3414  }
3415  else // == 3
3416  {
3417  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3418  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3419  }
3420  }
3421  else
3422  {
3423  if(EntryPos > 1)
3424  {
3425  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3426  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3427  }
3428  else
3429  {
3430  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3431  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3432  }
3433  }
3434  EntryHalfLength = CurrentElementHalfLength;
3435  FrontElementLength = 2 * CurrentElementHalfLength;
3436  }
3437  else
3438  {
3439  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3440  }
3441  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3442  {
3443  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3444  }
3445 
3446  // check if zero entry speed with another train directly in front & if so remain stopped
3447  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3448  {
3449  EntrySpeed = 0;
3450  ExitSpeedHalf = 0;
3451  ExitSpeedFull = 0;
3452  MaxExitSpeed = 0;
3453  BrakeRate = 0;
3454  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3455  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3456  StoppedForTrainInFront = true;
3457  Utilities->CallLogPop(705);
3458  return;
3459  }
3460 
3461  // new at v2.4.0 - check for stopped and zero power
3462  if((EntrySpeed < 1) && PowerAtRail < 1)
3463  {
3464  EntrySpeed = 0;
3465  ExitSpeedHalf = 0;
3466  ExitSpeedFull = 0;
3467  MaxExitSpeed = 0;
3468  BrakeRate = 0;
3469  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3470  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3471  StoppedWithoutPower = true;
3472  Utilities->CallLogPop(2125);
3473  return;
3474  }
3475 
3476 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3477  if(BeingCalledOn)
3478  {
3479  LimitingSpeed = CallOnMaxSpeed;
3480  }
3481  else
3482  {
3483  LimitingSpeed = MaximumSpeedLimit;
3484  }
3485  if(LimitingSpeed > FrontElementSpeedLimit)
3486  LimitingSpeed = FrontElementSpeedLimit;
3487  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3488  LimitingSpeed = MaxRunningSpeed;
3489  FrontElementMaxSpeed = LimitingSpeed;
3490 
3491 /*
3492  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3493  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3494  (2) V/3.6 = U/3.6 - FT;
3495  (3) S = UT/3.6 - 0.5FT^2
3496  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3497 
3498  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3499  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3500  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3501  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3502  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3503 
3504  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3505  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3506  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3507 */
3508 
3509 // check if running past a red signal without permission
3510  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3511  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3512  {
3513  SPADFlag = true; // user has to intervene to reset & restart after spad
3514  }
3515 
3516  if(!SPADFlag)
3517  {
3518  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3519 
3520  double ExitSpeedAtMaxBraking;
3521  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3522  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3524  {
3525  ExitSpeedAtMaxBraking = 0;
3526  }
3527  else
3528  {
3529  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3530  }
3531  double SpeedToUse;
3532  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added after v2.4.1 because trains entering at a continuation with zero (or very low) speed
3533  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3534  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3535  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3536  {
3537  SpeedToUse = ExitSpeedAtMaxBraking;
3538  }
3539  else
3540  {
3541  SpeedToUse = LimitingSpeed;
3542  }
3543  if(ExitSpeedFull > SpeedToUse)
3544  {
3545  ExitSpeedFull = SpeedToUse;
3546  }
3547  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3548  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3549 
3550  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3551  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3552  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3553 
3554  do
3555  {
3556  RedSignalFlag = false;
3557  BuffersFlag = false;
3558  StationFlag = false;
3559  BuffersOrContinuationNowFlag = false;
3560  ContinuationNextFlag = false;
3561  // have to reset this after the above test
3562  // add current element length to CumulativeLength
3563  CumulativeLength += (2 * CurrentElementHalfLength);
3564  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3565  {
3566  SignallerStopRequired = true;
3567  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3568  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3569  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3570  if(SignallerStopBrakeRate < TempBR)
3571  {
3572  SignallerStopBrakeRate = TempBR;
3573  }
3574  }
3575  // first check for stops within the length of the current element, where don't want any more checks & don't want
3576  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3577  // during the last loop when the NextTrackVectorPosition was the signal.
3578 
3579  // check if current element is a buffer
3580  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3581  {
3582  // no need to add in the length of this element to CumulativeLength as already included
3583  BuffersFlag = true;
3584  }
3585  // check if current element is a station stop
3586  if(TrainMode == Timetable)
3587  {
3588  bool StopRequired = false;
3589  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3590  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3591  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3592  {
3593  // no need to add in the length of element to CumulativeLength
3594  if(StopRequired)
3595  StationFlag = true;
3596  }
3597  }
3598  else
3599  {
3600  StationFlag = false;
3601  }
3602 
3603  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3604  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3605  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3606  {
3607  BuffersOrContinuationNowFlag = true;
3608  }
3609  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3610  {
3611  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3612  {
3613  if((EntryPos == 0) || (EntryPos == 2))
3614  {
3615  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3616  ExitPos = 1;
3617  else
3618  ExitPos = 3;
3619  }
3620  else
3621  ExitPos = 0;
3622  }
3623  else
3624  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3625  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3626  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3627  if(NextTrackVectorPosition > -1)
3628  {
3629  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3630  // this test & section added at v0.6
3631  {
3632  if((NextEntryPos == 0) || (NextEntryPos == 2))
3633  {
3634  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3635  {
3636  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3637  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3638  }
3639  else
3640  {
3641  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3642  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3643  }
3644  }
3645  else if(NextEntryPos == 1)
3646  {
3647  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3648  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3649  }
3650  else // == 3
3651  {
3652  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3653  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3654  }
3655  }
3656  else
3657  {
3658  if(NextEntryPos > 1)
3659  {
3660  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3661  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3662  }
3663  else
3664  {
3665  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3666  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3667  }
3668  }
3669  }
3670  else
3671  {
3672  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3673  }
3674  // now check for stops, first cover those where don't want to add in length of next element
3675  // check if next element is a red signal - Attr 0,
3676  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3677  // CurrentTrackVectorPosition not NextTrackVectorPosition
3678  bool StopRequired;
3679  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3680  {
3681  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3682  // no need to add in the length of element to CumulativeLength
3683  RedSignalFlag = true;
3684  // next element is a red signal
3685  }
3686  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3687  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3688  // at least one platform element free
3690  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3691  NextEntryPos, TrainID))
3692  {
3693  // no need to add in the length of element to CumulativeLength
3694  if(StopRequired)
3695  StationFlag = true;
3696  }
3697  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3698  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3699  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3700  {
3701  // no need to add in the length of element to CumulativeLength
3702  TrainInFrontInSignallerModeFlag = true;
3703  }
3704  // check if next element is a buffer
3705  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3706  {
3707  // need to add in the length of that element to CumulativeLength
3708  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3709  BuffersFlag = true;
3710  }
3711  // check if next element is a station stop
3713  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3714  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3715  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3716  {
3717  // need to add in the length of that element to CumulativeLength if a stop required
3718  if(StopRequired)
3719  {
3720  StationFlag = true;
3721  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3722  }
3723  }
3724  }
3725  //now can decide whether need to stop over CumulativeLength
3726  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3727  {
3728  // have to come to a stop over CumulativeLength
3729  if(CumulativeLength == FrontElementLength)
3730  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3731  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3732  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3733  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3734  // if not calc speed at halfway point & if less than above set half speed to this value;
3735  // use constant acceleration in calculating half time point
3736  {
3737  MaxExitSpeed = 0;
3738  double MaxHalfSpeed;
3739  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3740  // have to halve the element length, & can't be zero or negative so no need to test
3741  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3742  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3743  MaxHalfSpeed = FrontElementMaxSpeed;
3744  else
3745  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3746  if(MaxHalfSpeed > (2 * EntrySpeed))
3747  // use 2x to prevent kangarooing at last element when had
3748  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3749  {
3750  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3751  0.333334);
3752  bool HalfSpeedLimited = false;
3753  if(MaxHalfSpeed < ExitSpeedHalf)
3754  {
3755  ExitSpeedHalf = MaxHalfSpeed;
3756  HalfSpeedLimited = true;
3757  }
3758  if(PowerAtRail > 1)
3759  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3760  { // [km/h/3.6 = m/s]
3761  ExitTimeHalf =
3762  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3763  }
3764  else
3765  {
3766  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3767  }
3768  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3769  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3770  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3771  // by a long braking period
3772  ExitSpeedFull = 0;
3773  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3774  if(TempBrakeRate > MaxBrakeRate)
3775  TempBrakeRate = MaxBrakeRate;
3776  // shouldn't be but leave in anyway
3777  if(TempBrakeRate > BrakeRate)
3778  BrakeRate = TempBrakeRate;
3779  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3780  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3781  if(HalfSpeedLimited)
3782  // this is the change referred to above
3783  {
3784  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3785  ExitTimeHalf = EntryTime + BrakingTime;
3786  ExitTimeFull = ExitTimeHalf + BrakingTime;
3787  }
3788  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3789  Utilities->CallLogPop(1095);
3790  return;
3791  }
3792  }
3793  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3794  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3795  // calc th, tf, sh, & sf
3796  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3797  if(TempBrakeRate > MaxBrakeRate)
3798  TempBrakeRate = MaxBrakeRate;
3799  if(TempBrakeRate > BrakeRate)
3800  BrakeRate = TempBrakeRate;
3801  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3802  if(SignallerStopRequired)
3803  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
3804  {
3806  {
3808  // this prevents the brakerate from reducing for a signaller stop
3809  // regardless of other conditions that may change as progress round the loop
3810  }
3811  }
3813  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
3814  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
3815  {
3817  }
3818  int TempMaxExitSpeed;
3819  // calc current value & if less than MaxExitSpeed set that to this
3820  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
3821  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3822  MaxExitSpeedAtHalfBraking = 0;
3823  else
3824  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3825  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
3826  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
3827  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3828  TempMaxExitSpeed = FrontElementMaxSpeed;
3829  else
3830  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3831  if(TempMaxExitSpeed < MaxExitSpeed)
3832  MaxExitSpeed = TempMaxExitSpeed;
3833 
3834  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
3835  // Cumulativelength, and Cumulativelength
3836 
3837  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
3838  {
3839  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
3840  if(ExitSpeedHalfSquared < 10)
3841  ExitSpeedHalf = 0;
3842  else
3843  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3844  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
3845  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
3846  if(ExitSpeedFullSquared < 10)
3847  ExitSpeedFull = 0;
3848  else
3849  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
3850  if((StationFlag) && (CumulativeLength == FrontElementLength))
3851  {
3852  ExitSpeedFull = 0;
3853  // force a stop for station (not for buffers or red signal)
3854  }
3855  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
3856  }
3857  // new condition at v2.4.0
3858  else if(PowerAtRail <= 1)
3859  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
3860  // avoid using AValue in denominator or have excessively long times
3861  {
3862  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
3863  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3864  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
3865 
3866  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
3867  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
3868  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
3869  }
3870  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
3871  // without the power need above condition or have hours of delay times, above added at v2.4.0
3872  {
3873  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3874  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3875  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3876  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3877  BrakeRate = 0;
3878  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3879  0.333334);
3880  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3881  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
3883  // can accelerate continually over the half length
3884  {
3885  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
3886  / 86400.0);
3888  // can accelerate continually over the full length
3889  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
3890  {
3891  ExitTimeFull =
3892  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3893  }
3894  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
3895  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
3896  {
3897  // added at v0.6 as a safeguard
3898  if(MaxExitSpeed < EntrySpeed)
3900  // to prevent DeltaExitTimeToMaxInSecs being negative
3901  if(MaxExitSpeed < 1)
3902  MaxExitSpeed = 1;
3903  // to prevent divide by zero error
3904  // below as was
3906  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
3907  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
3908  (1.5 * AValue * AValue);
3909  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
3910  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
3911  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
3912  }
3913  }
3914  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
3915  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
3916  // second halves of the element
3917  {
3918  // added at v0.6 as a safeguard
3919  if(MaxExitSpeed < EntrySpeed)
3921  // to prevent DeltaExitTimeToMaxInSecs being negative
3922  if(MaxExitSpeed < 1)
3923  MaxExitSpeed = 1; // to prevent divide by zero error
3924  // below as was
3926  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
3927  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
3928  (1.5 * AValue * AValue);
3929  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
3930  // remaining distance to half length
3931  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
3932  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
3934  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
3935  }
3936  }
3937  Utilities->CallLogPop(706);
3938  return;
3939  }
3940  else
3941  {
3942  if(!BuffersOrContinuationNowFlag)
3943  {
3944  if(NextSpeedLimit < LimitingSpeed)
3945  LimitingSpeed = NextSpeedLimit;
3946  }
3947  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
3948  int TempMaxExitSpeed;
3949  // calc current value & if less than MaxExitSpeed set that to this
3950  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
3951  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
3952  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3953  MaxExitSpeedAtHalfBraking = 0;
3954  else
3955  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3956  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3957  TempMaxExitSpeed = FrontElementMaxSpeed;
3958  else
3959  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3960  if(TempMaxExitSpeed < MaxExitSpeed)
3961  MaxExitSpeed = TempMaxExitSpeed;
3962  // MaxExitSpeed is an external variable & this can reduce its value
3963  if(EntrySpeed > LimitingSpeed)
3964  // note that LimitingSpeed is more restrictive than MaxExitSpeed
3965  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
3966  {
3967  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
3968  if(TempBrakeRate > MaxBrakeRate)
3969  TempBrakeRate = MaxBrakeRate;
3970  // shouldn't be for speedlimits since all known in advance, but include anyway
3971  if(TempBrakeRate > BrakeRate)
3972  BrakeRate = TempBrakeRate;
3973  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3974  }
3975  }
3976  if(!BuffersOrContinuationNowFlag)
3977  {
3978  CurrentTrackVectorPosition = NextTrackVectorPosition;
3979  EntryPos = NextEntryPos;
3980  CurrentElementHalfLength = NextElementHalfLength;
3981  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
3982  {
3983  ContinuationNextFlag = true;
3984  }
3985  }
3986  }
3987  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
3989  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
3990  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
3991  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
3992  // stopping distance after it.
3993 
3994  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
3995  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
3996 
3997  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
3998  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
3999  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4000  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4001  // too late
4002 
4003  // set final braking or acc'n speed & time values
4004  if(BrakeRate > 0.01)
4005  {
4006  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4007  if(ExitSpeedHalfSquared < 10)
4008  ExitSpeedHalf = 0;
4009  else
4010  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4011  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4012  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4013  if(ExitSpeedFullSquared < 10)
4014  ExitSpeedFull = 0;
4015  else
4016  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4017  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4018  }
4019  else
4020  {
4021  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4022  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4023  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4024  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4025 
4026  BrakeRate = 0;
4027  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4028  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4029  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4031  {
4032  if(PowerAtRail > 1)
4033  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4034  { // [km/h/3.6 = m/s]
4035  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4036  / 86400.0);
4037  }
4038  else
4039  {
4040  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4041  }
4042 
4044  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4045  {
4046  if(PowerAtRail > 1)
4047  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4048  { // [km/h/3.6 = m/s]
4049  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4050  / 86400.0);
4051  }
4052  else
4053  {
4054  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4055  }
4056  }
4057  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4058  {
4059  // added at v0.6 as a safeguard
4060  if(MaxExitSpeed < EntrySpeed)
4062  // to prevent DeltaExitTimeToMaxInSecs being negative
4063  if(MaxExitSpeed < 1)
4064  MaxExitSpeed = 1; // to prevent divide by zero error
4065  // below as was
4067  double DeltaExitTimeToMaxInSecs;
4068  double DistanceToMax;
4069  if(PowerAtRail > 1) // added at v2.4.0
4070  {
4071  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4072  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4073  (1.5 * AValue * AValue);
4074  }
4075  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4076  {
4077  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4078  // these not really accurate but will be good enough
4079  DistanceToMax = EntryHalfLength;
4080  }
4081  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4082  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4083  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4084  }
4085  }
4086  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4087  {
4088  // added at v0.6 as a safeguard
4089  if(MaxExitSpeed < EntrySpeed)
4091  // to prevent DeltaExitTimeToMaxInSecs being negative
4092  if(MaxExitSpeed < 1)
4093  MaxExitSpeed = 1; // to prevent divide by zero error
4094  // below as was
4096  double DeltaExitTimeToMaxInSecs;
4097  double DistanceToMax;
4098  if(PowerAtRail > 1) // added at v2.4.0
4099  {
4100  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4101  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4102  (1.5 * AValue * AValue);
4103  }
4104  else
4105  {
4106  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4107  // these not really accurate but will be good enough
4108  DistanceToMax = EntryHalfLength / 2;
4109  }
4110  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4111  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4112  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4114  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4115  }
4116  }
4117  }
4118 
4119  else // SPADFlag set
4120  {
4122  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4123  if(ExitSpeedHalfSquared < 10)
4124  ExitSpeedHalf = 0;
4125  else
4126  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4127  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4128  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4129  if(ExitSpeedFullSquared < 10)
4130  ExitSpeedFull = 0;
4131  else
4132  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4133  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4134 
4135  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4136  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4137  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4138  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4139  // will be the stopping speed.
4140  if(ExitSpeedFull > 0)
4141  {
4142  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4143  {
4144  if((EntryPos == 0) || (EntryPos == 2))
4145  {
4146  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4147  ExitPos = 1;
4148  else
4149  ExitPos = 3;
4150  }
4151  else
4152  ExitPos = 0;
4153  }
4154  else
4155  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4156  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4157  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4158  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4159  {
4160  int NextElementLength;
4161  if(NextEntryPos > 1)
4162  {
4163  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4164  }
4165  else
4166  {
4167  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4168  }
4169  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4170  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4171  {
4172  ExitSpeedFull = 0;
4173  }
4174  }
4175  }
4176  }
4177  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4178  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4179  { // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4180  if(LeadElement > -1)
4181  {
4183  // don't stop on a continuation either entering or leaving
4184  {
4187  MaxExitSpeed = LimitingSpeed;
4188  BrakeRate = 0;
4189  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4190  // assume length is 50m for ease of calc
4191  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4192  Utilities->CallLogPop(2126);
4193  return;
4194  }
4195  }
4196  else if(MidElement > -1)
4197  {
4199  {
4202  MaxExitSpeed = LimitingSpeed;
4203  BrakeRate = 0;
4204  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4205  // assume length is 50m for ease of calc
4206  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4207  Utilities->CallLogPop(2127);
4208  return;
4209  }
4210  }
4211  else if(LagElement > -1)
4212  {
4214  {
4217  MaxExitSpeed = LimitingSpeed;
4218  BrakeRate = 0;
4219  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4220  // assume length is 50m for ease of calc
4221  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4222  Utilities->CallLogPop(2128);
4223  return;
4224  }
4225  }
4226 
4227  if(EntrySpeed > 7.5) // keep going for at least another element
4228  {
4229  ExitSpeedHalf = EntrySpeed - 2.5;
4230  ExitSpeedFull = EntrySpeed - 5;
4231  MaxExitSpeed = LimitingSpeed;
4232  BrakeRate = 0;
4233  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4234  // assume length is 50m for ease of calc
4235  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4236  Utilities->CallLogPop(2129);
4237  return;
4238  }
4239  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4240  { // will appear to have slowed at steady rate
4241  ExitSpeedHalf = 0;
4242  ExitSpeedFull = 0;
4243  MaxExitSpeed = LimitingSpeed;
4244  BrakeRate = 0;
4245  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4246  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4247  StoppedWithoutPower = true;
4248  Utilities->CallLogPop(2130);
4249  return;
4250  }
4251  }
4252 
4253  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4254  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4255  Utilities->CallLogPop(707);
4256 }
4257 // ---------------------------------------------------------------------------
4258 /*
4259  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4260  {
4261  int NextExitPos;
4262  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4263  if(TimetableVector.empty()) return false;
4264  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4265  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4266  {
4267  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4268  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4269  {
4270  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4271  }
4272  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4273  {
4274  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4275  }
4276  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4277  NextElement = TempElement;
4278  }
4279  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4280  if(NextElement.TrackType == Buffers) return true;
4281  return false;//shouldn't reach here but include to prevent compiler return warning
4282  }
4283 */
4284 // ---------------------------------------------------------------------------
4285 
4286 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4287 /*
4288  returns the number by which the train ActionVectorEntryPtr needs
4289  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4290  actions when a stop or pass location has been reached before other timetabled actions have been carried out. If can't find it, or Name
4291  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4292  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4293  or pass (false) the location.
4294 */ {
4295  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4296  Stop = false;
4297  if(TimetableFinished || (Name == ""))
4298  {
4299  Utilities->CallLogPop(957);
4300  return -1;
4301  }
4302  // start looking from current pointer position
4303  for(TActionVectorEntry * Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4304  {
4305  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4306  {
4307  break;
4308  }
4309  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4310  {
4311  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4312  {
4313  Stop = true;
4314  Utilities->CallLogPop(960);
4315  return (Ptr - ActionVectorEntryPtr);
4316  }
4317  }
4318  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4319  {
4320  Utilities->CallLogPop(1517);
4321  return (Ptr - ActionVectorEntryPtr);
4322  }
4323  }
4324  Utilities->CallLogPop(959);
4325  return -1; // not found a valid entry
4326 }
4327 
4328 // ---------------------------------------------------------------------------
4329 
4331 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4332  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4333  Ignores the call-on signal.
4334 */ {
4335  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4336  int ReturnVal = 0;
4337 
4338  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4339  {
4340  Track->TrackVector.at(x).TempTrackMarker01 = false;
4341  Track->TrackVector.at(x).TempTrackMarker23 = false;
4342  }
4343  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4344  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4345 
4346  while(true)
4347  {
4348  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4349  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4350  {
4351  ReturnVal = 1;
4352  break;
4353  }
4354  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4355  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4356  {
4357  ReturnVal = 2;
4358  break;
4359  }
4360  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4361  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4362  {
4363  ReturnVal = 3;
4364  break;
4365  }
4366  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4367  CurrentTrackVectorPosition).TrackType == Crossover))
4368  {
4369  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4370  // must be a loop - reached same point as examined earlier
4371  {
4372  ReturnVal = 4;
4373  break;
4374  }
4375  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4376  {
4377  ReturnVal = 4;
4378  break;
4379  }
4380  }
4381  else
4382  {
4383  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4384  CurrentTrackVectorPosition).TempTrackMarker23))
4385  {
4386  ReturnVal = 4;
4387  break;
4388  }
4389  }
4390  if(EntryPos < 2)
4391  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4392  else
4393  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4394 
4395  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4396  {
4397  if((EntryPos == 0) || (EntryPos == 2))
4398  {
4399  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4400  ExitPos = 1;
4401  else
4402  ExitPos = 3;
4403  }
4404  else
4405  ExitPos = 0;
4406  }
4407  else
4408  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4409  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4410  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4411  CurrentTrackVectorPosition = NextTrackVectorPosition;
4412  EntryPos = NextEntryPos;
4413  }
4414  if(ReturnVal == 1)
4415  {
4416  Utilities->CallLogPop(708);
4417  return false;
4418  }
4419  if(ReturnVal == 2)
4420  {
4421  Utilities->CallLogPop(709);
4422  return true;
4423  }
4424  if(ReturnVal == 3)
4425  {
4426  Utilities->CallLogPop(946);
4427  return true;
4428  }
4429  if(ReturnVal == 4)
4430  {
4431  Utilities->CallLogPop(947);
4432  return true;
4433  }
4434  else
4435  {
4436  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4437  }
4438 }
4439 
4440 // ---------------------------------------------------------------------------
4441 
4443 /*
4444  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4445  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4446  change of direction (cdt), remaining here (Frh), or under signaller control);
4447  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4448  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4449  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4450  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] and l) no existing route conflicts with the route into the platform
4451  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4452  change points outside the route or have a route conflict if another route is set.
4453 */ {
4454  if(Track->RouteFlashFlag)
4455  return false;
4456 
4457  // don't want to create a new route from the stop signal if one is already in construction as
4458  // some of the callingon route elements may be involved
4459  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4460  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4461  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4462  int RouteStartPosition;
4463  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4464  int PlatformPosition;
4465  // the track vector position of the first stop platfrom
4466  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4467  // not used here
4468  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4469 
4470  // must be stopped at a signal but not at a location & still in timetable (a)
4471 
4473  // no need to check for SignallerStopped as this function only called in Timetable mode
4474  {
4475  Utilities->CallLogPop(711);
4476  return false;
4477  }
4478  while(true)
4479  {
4480  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4481  // don't look further than 4km ahead (j)
4482  if(Distance > 4000)
4483  {
4484  Utilities->CallLogPop(967);
4485  return false;
4486  }
4487  // if find another train on an element in front, before find a valid platform, return false (c)
4488  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4489  {
4490  Utilities->CallLogPop(713);
4491  return false;
4492  }
4493  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4494  // be facing later on)
4495  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4496  {
4497  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4498  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4499  if(OtherTrain.LeadElement == -1)
4500  {
4501  Utilities->CallLogPop(714);
4502  return false;
4503  }
4504  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4505  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4506  {
4507  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4508  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4509  (OtherTrain.TrainMode == Signaller))
4510  {
4511  break;
4512  }
4513  else
4514  {
4515  Utilities->CallLogPop(955);
4516  return false;
4517  }
4518  }
4519  else // (h)
4520  {
4521  break;
4522  }
4523  }
4524  // if reach buffers or exit continuation return false (can set route)
4525  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4526  {
4527  Utilities->CallLogPop(716);
4528  return false;
4529  }
4530  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4531  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4533  {
4534  Utilities->CallLogPop(717);
4535  return false;
4536  }
4537  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4538 /*
4539  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4540  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4541  {
4542  Utilities->CallLogPop(718);
4543  return false;
4544  }
4545 */
4546  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4547  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4548  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4549  {
4550  if(StopRequired)
4551  {
4552  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4553  {
4554  if(!PlatformFoundFlag)
4555  PlatformPosition = CurrentTrackVectorPosition;
4556  // ensure this only set once at first valid platform position - the unrestricted route will end here
4557  PlatformFoundFlag = true;
4558  }
4559  }
4560  }
4561  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4562  // train has to be at station but that has to be before the next forward signal
4563 /*
4564  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4565  {
4566  Utilities->CallLogPop(719);
4567  return false;
4568  }
4569 */
4570  // make sure points are followed correctly (d) & set ExitPos
4571  if(CurrentTrackElement.TrackType == Points)
4572  {
4573  if((EntryPos == 0) || (EntryPos == 2))
4574  {
4575  if(CurrentTrackElement.Attribute == 0)
4576  ExitPos = 1;
4577  else
4578  ExitPos = 3;
4579  }
4580  if(EntryPos == 1)
4581  {
4582  if(CurrentTrackElement.Attribute == 0)
4583  ExitPos = 0;
4584  else
4585  {
4586  Utilities->CallLogPop(720);
4587  return false;
4588  }
4589  }
4590  if(EntryPos == 3)
4591  {
4592  if(CurrentTrackElement.Attribute == 1)
4593  ExitPos = 0;
4594  else
4595  {
4596  Utilities->CallLogPop(721);
4597  return false;
4598  }
4599  }
4600  }
4601  else
4602  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4603 
4604  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4605  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4606  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4607  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4608  if(ElementNumber < 2)
4609  SkipRouteCheck = true;
4610  else
4611  SkipRouteCheck = false;
4612  if(ElementNumber == 1) // the stop signal
4613  {
4614  RouteStartPosition = CurrentTrackVectorPosition;
4615  }
4616 /*
4617  if(ElementNumber == 2)
4618  {
4619  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4620  else AutoSigs = false;
4621  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4622  }
4623 */
4624  if(ElementNumber > 1)
4625  {
4626  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4627  RouteOrPartRouteSet = true;
4628  else
4629  RouteOrPartRouteSet = false;
4630  }
4631  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4632  {
4633  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4634  {
4635  Utilities->CallLogPop(1859);
4636  return false;
4637  }
4638  int ExitLink = CurrentTrackElement.Link[ExitPos];
4639  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4640  {
4641  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4642  {
4643  Utilities->CallLogPop(1850);
4644  return false;
4645  }
4646  }
4647  }
4648 
4649  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4650  if(EntryPos < 2)
4651  Distance += CurrentTrackElement.Length01;
4652  else
4653  Distance += CurrentTrackElement.Length23;
4654  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4655  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4656  CurrentTrackVectorPosition = NextTrackVectorPosition;
4657  EntryPos = NextEntryPos;
4658  ElementNumber++;
4659  } // while(true)
4660 
4661  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4662  // from the stop signal (note that it may be last in an autosigs route)
4663  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4664  // this in ClockTimer2)
4665 
4666  // now add elements to the CallonVector
4667  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4668 
4669  AllRoutes->CallonVector.push_back(CallonEntry);
4670  Utilities->CallLogPop(1860);
4671  return true; // return false if fail to set route for any reason
4672 }
4673 
4674 // ---------------------------------------------------------------------------
4675 /*
4676  bool TTrain::TimetableFinished()
4677  {
4678  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4679  {
4680  return true;
4681  }
4682  return false;
4683  }
4684 */
4685 // ---------------------------------------------------------------------------
4686 
4687 AnsiString TTrain::GetTrainHeadCode(int Caller)
4688 
4689 {
4690  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
4691  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
4692 
4693  Utilities->CallLogPop(1452);
4694  return RepeatHeadCode;
4695 }
4696 
4697 // ---------------------------------------------------------------------------
4698 
4699 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
4700 {
4701  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
4702  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
4703 
4704  Utilities->CallLogPop(1453);
4705  return RepeatTime;
4706 }
4707 
4708 // ---------------------------------------------------------------------------
4709 
4710 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
4711 { // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
4712  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
4713  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
4714  // first check that train is fully on the railway
4715  bool FrontValid = false, RearValid = false;
4716  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
4717 
4718  if((LeadElement == -1) || (MidElement == -1))
4719  {
4720  TrainToBeJoinedBy = NULL;
4721  Utilities->CallLogPop(2131);
4722  return false;
4723  }
4725  {
4726  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
4727  FrontValid = true;
4728  }
4730  {
4731  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
4732  RearValid = true;
4733  }
4734  int TrainToBeJoinedByID = -1;
4735 
4736  // first check if on a 2-track element & select correct ID number if so
4737  if(FrontValid)
4738  {
4739  if(FrontAdjacentTrackElement.TrackType == Bridge)
4740  {
4742  {
4743  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4744  }
4745  else
4746  {
4747  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4748  }
4749  }
4750  else
4751  {
4752  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
4753  }
4754  }
4755  if((TrainToBeJoinedByID < 0) && RearValid)
4756  {
4757  // first check if on a 2-track element & select correct ID number if so
4758  if(RearAdjacentTrackElement.TrackType == Bridge)
4759  {
4761  {
4762  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4763  }
4764  else
4765  {
4766  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4767  }
4768  }
4769  else
4770  {
4771  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
4772  }
4773  }
4774  if(TrainToBeJoinedByID < 0) // no adjacent train
4775  {
4776  TrainToBeJoinedBy = NULL;
4777  Utilities->CallLogPop(2132);
4778  return false;
4779  }
4780  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
4781  if(!TrainToBeJoinedBy->Stopped())
4782  {
4783  TrainToBeJoinedBy = NULL;
4784  Utilities->CallLogPop(2133);
4785  return false;
4786  }
4787  Utilities->CallLogPop(2134);
4788  return true;
4789 }
4790 
4791 // ---------------------------------------------------------------------------
4792 
4793 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
4794  TDateTime TimetableNonRepeatTime, bool Warning)
4795 /*
4796  Time = timetable time, the time adjustments for repeat trains is carried out internally
4797  Not all messages need this, if not needed a dummy value is required but not used
4798 
4799  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
4800  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
4801  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
4802  //NB for Frh just give terminated message but without event time - don't use this function
4803  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
4804  Create: 06:05:40: 2F46 created at Old Street 1 minute late
4805  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
4806  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
4807  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
4808  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
4809  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
4810  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
4811  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
4812  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
4813  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
4814  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
4815  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
4816  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
4817  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
4818  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
4819  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
4820  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
4821  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
4822  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
4823  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
4824  SignallerStop 06:05:40: 2F46 stopped on signaller command
4825  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
4826  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
4827 */ {
4828  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
4829  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
4830  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
4831  int IntMinsLate = 0;
4832 
4833  // need to set it in case MinsLate == 0, since it isn't tested for that
4834  if(ActionType == Arrive)
4835  ActionLog = " arrived at ";
4836  if(ActionType == Terminate)
4837  {
4838  if(TerminatedMessageSent) // to avoid it being sent twice
4839  {
4840  Utilities->CallLogPop(1104);
4841  return;
4842  }
4843  ActionLog = " terminated at ";
4844  TerminatedMessageSent = true;
4845  }
4846  if(ActionType == Depart)
4847  ActionLog = " departed from ";
4848  if(ActionType == Pass)
4849  ActionLog = " passed ";
4850  if(ActionType == Create)
4851  ActionLog = " created at ";
4852  if(ActionType == Enter)
4853  ActionLog = " entered railway at ";
4854  if(ActionType == Leave)
4855  ActionLog = " left railway at ";
4856  if(ActionType == FrontSplit)
4857  ActionLog = " split from front to ";
4858  if(ActionType == RearSplit)
4859  ActionLog = " split from rear to ";
4860  if(ActionType == JoinedByOther)
4861  ActionLog = " joined by ";
4862  if(ActionType == ChangeDirection)
4863  ActionLog = " changed direction at ";
4864  if(ActionType == NewService)
4865  ActionLog = " became new service ";
4866  if(ActionType == TakeSignallerControl)
4867  ActionLog = " taken under signaller control at ";
4868  if(ActionType == RestoreTimetableControl)
4869  ActionLog = " restored to timetable control at ";
4870  if(ActionType == RemoveTrain)
4871  {
4872  if(Crashed)
4873  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
4874  else if(Derailed)
4875  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
4876  else
4877  ActionLog = " REMOVED FROM RAILWAY at ";
4878  }
4879  if(ActionType == SignallerMoveForwards)
4880  ActionLog = " received signaller authority to proceed";
4881  if(ActionType == SignallerStepForward)
4882  ActionLog = " received signaller authority to step forward";
4883  if(ActionType == SignallerChangeDirection)
4884  ActionLog = " changed direction under signaller control at ";
4885  if(ActionType == SignallerPassRedSignal)
4886  ActionLog = " received signaller authority to pass red signal";
4887  if(ActionType == SignallerControlStop)
4888  ActionLog = " received signaller instruction to stop";
4889  if(ActionType == SignallerStop)
4890  ActionLog = " stopped on signaller instruction ";
4891  if(ActionType == SignallerJoin)
4892  ActionLog = " joined under signaller control by ";
4893  if(ActionType == TrainFailure)
4894  ActionLog = " suffered an onboard power failure at ";
4895  if(ActionType == RepairFailedTrain)
4896  ActionLog = " failure repaired at ";
4897  if(ActionType == SignallerLeave)
4898  ActionLog = " left railway under signaller control at ";
4899  if(OtherHeadCode != "")
4900  OtherHeadCode += " at ";
4901  TDateTime ActualTime = TrainController->TTClockTime;
4902 
4903  if(Warning)
4904  {
4905  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
4906  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
4907  }
4908  else
4909  {
4910  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
4911  }
4912  bool TimePerformance = true;
4913 
4914  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
4915  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
4916  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
4917  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
4918  // SignallerJoin & RepairFailedTrain new at v2.4.0
4919  {
4920  TimePerformance = false;
4921  }
4922  if(TimePerformance)
4923  {
4924  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
4925  MinsDelayed = float(MinsLate);
4926  // new v2.2.0 for OpActionPanel, can be positive or negative
4927  if(ActionType == Arrive)
4929  // since train has just arrived this value is the
4930  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
4931  // subtracted from later stop recoverable times.
4932  if(MinsLate < 0)
4933  {
4934  IntMinsLate = int(ceil(MinsLate));
4935  }
4936  if(MinsLate > 0)
4937  {
4938  IntMinsLate = int(floor(MinsLate));
4939  }
4940  if(IntMinsLate == 0)
4941  {
4942  PerfLog = " on time";
4943  }
4944  else if(IntMinsLate == 1)
4945  PerfLog = " 1 minute late";
4946  else if(IntMinsLate == -1)
4947  PerfLog = " 1 minute early";
4948  else if(IntMinsLate > 1)
4949  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
4950  else if(IntMinsLate < -1)
4951  {
4952  int PosIntMinsLate = -IntMinsLate;
4953  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
4954  }
4955  if(LocationName.Pos('-') > 0)
4956  {
4957  PerfLog = "," + PerfLog;
4958  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
4959  }
4960  Display->PerformanceLog(0, BaseLog + PerfLog);
4961  }
4962  else
4963  Display->PerformanceLog(1, BaseLog);
4964  if(Warning)
4965  {
4966  Display->WarningLog(0, WarningBaseLog);
4967  }
4968 
4969  // update statistics
4970  if((ActionType == Arrive) && (IntMinsLate == 0))
4971  {
4973  }
4974  else if((ActionType == Arrive) && (IntMinsLate > 0))
4975  {
4977  TrainController->TotLateArrMins += IntMinsLate;
4978  }
4979  else if((ActionType == Arrive) && (IntMinsLate < 0))
4980  {
4982  TrainController->TotEarlyArrMins += abs(IntMinsLate);
4983  }
4984  else if((ActionType == Pass) && (IntMinsLate == 0))
4985  {
4987  }
4988  else if((ActionType == Pass) && (IntMinsLate > 0))
4989  {
4991  TrainController->TotLatePassMins += IntMinsLate;
4992  }
4993  else if((ActionType == Pass) && (IntMinsLate < 0))
4994  {
4996  TrainController->TotEarlyPassMins += abs(IntMinsLate);
4997  }
4998  else if((ActionType == Depart) && (IntMinsLate == 0))
4999  {
5001  }
5002  else if((ActionType == Depart) && (IntMinsLate > 0))
5003  {
5005  TrainController->TotLateDepMins += IntMinsLate;
5006  }
5007  Utilities->CallLogPop(968);
5008 }
5009 
5010 // ---------------------------------------------------------------------------
5011 
5012 void TTrain::TrainHasFailed(int Caller)
5013 {
5014  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5015  if(Crashed || Derailed || DerailPending)
5016  {
5017  TrainFailurePending = false;
5018  Utilities->CallLogPop(2135);
5019  return;
5020  }
5021  AnsiString LocName = "";
5022 
5023  if(LeadElement > -1)
5024  {
5026  }
5027  if((LocName == "") && (MidElement > -1))
5028  {
5030  }
5031  if((LocName == "") && LeadElement > -1)
5032  {
5033  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5034  }
5035  if((LocName == "") && (MidElement > -1))
5036  {
5037  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5038  }
5039  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5040  TrainFailed = true;
5041  TrainFailurePending = false;
5043  PowerAtRail = 0.08;
5044  AValue = sqrt(2 * PowerAtRail / Mass);
5046  // TrainFailed only called when PlotElements properly set to lead, Mid & Lag elements
5047  if(Stopped())
5048  {
5049  EntrySpeed = 0;
5050  ExitSpeedHalf = 0;
5051  ExitSpeedFull = 0;
5052  MaxExitSpeed = 0;
5053  BrakeRate = 0;
5054  StoppedWithoutPower = true;
5055  }
5057  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5058  // true for warning, TDateTime not used
5059  Utilities->CallLogPop(2136);
5060 }
5061 
5062 // ---------------------------------------------------------------------------
5063 
5064 void TTrain::FrontTrainSplit(int Caller)
5065 {
5066 /*
5067  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5068  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5069  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5070  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5071 */
5072  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5073  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5074  if(PowerAtRail < 1)
5075  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5076  {
5078  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5080  Utilities->CallLogPop(2137);
5081  return;
5082  }
5083  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5084 
5085  if(LocationName == "")
5086  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5087  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5088  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5089  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5091 
5092  // determine all positions & exits
5093  if(LocationName != "")
5094  {
5095  // if message given only call at ~5 sec intervals
5097  {
5098  FirstNamedElementPos = LeadElement;
5099  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5100  // check if possible with LeadElement as First
5101  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5102  {
5103  FirstNamedElementPos = MidElement;
5104  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5105  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5106  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5107  {
5109  {
5110  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5112  }
5113  Utilities->CallLogPop(1009);
5114  return;
5115  }
5116  }
5117  else
5118  {
5119  // if first is possible then check if all 4 positions at location, and if not try the second
5120  int LeadPosA = FirstNamedElementPos;
5121  int LeadPosB = FirstNamedLinkedElementPos;
5122  int LeadPosC = SecondNamedElementPos;
5123  int LeadPosD = SecondNamedLinkedElementPos;
5124  // count number of positions that are at the location
5125  int LeadNumAtLoc = 0;
5126  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5127  LeadNumAtLoc++;
5128  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5129  LeadNumAtLoc++;
5130  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5131  LeadNumAtLoc++;
5132  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5133  LeadNumAtLoc++;
5134  if(LeadNumAtLoc < 4)
5135  {
5136  FirstNamedElementPos = MidElement;
5137  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5138  SecondNamedLinkedElementPos)) // restore originals
5139  {
5140  FirstNamedElementPos = LeadPosA;
5141  FirstNamedLinkedElementPos = LeadPosB;
5142  SecondNamedElementPos = LeadPosC;
5143  SecondNamedLinkedElementPos = LeadPosD;
5144  }
5145  else // count number at location
5146  {
5147  int MidNumAtLoc = 0;
5148  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5149  MidNumAtLoc++;
5150  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5151  MidNumAtLoc++;
5152  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5153  MidNumAtLoc++;
5154  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5155  MidNumAtLoc++;
5156  if(LeadNumAtLoc > MidNumAtLoc)
5157  // change back, else keep new values
5158  {
5159  FirstNamedElementPos = LeadPosA;
5160  FirstNamedLinkedElementPos = LeadPosB;
5161  SecondNamedElementPos = LeadPosC;
5162  SecondNamedLinkedElementPos = LeadPosD;
5163  }
5164  }
5165  }
5166  }
5167  }
5168  else
5169  {
5170  Utilities->CallLogPop(1791);
5171  return;
5172  }
5173  }
5174  else
5175  throw Exception("Error - LocationName not set in FrontTrainSplit");
5176  // set front & rear train parameters
5177  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5178  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5179  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5180  if(LeadElement == FirstNamedElementPos)
5181  {
5182  if(MidElement == SecondNamedElementPos)
5183  {
5184  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5185  FrontTrainRearPosition = FirstNamedElementPos;
5186  RearTrainFrontPosition = SecondNamedElementPos;
5187  RearTrainRearPosition = SecondNamedLinkedElementPos;
5188  }
5189  else // MidElement must == FirstNamedLinkedElementPos
5190  {
5191  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5192  FrontTrainRearPosition = SecondNamedElementPos;
5193  RearTrainFrontPosition = FirstNamedElementPos;
5194  RearTrainRearPosition = FirstNamedLinkedElementPos;
5195  }
5196  }
5197  else // MidElement == FirstNamedElementPos
5198  {
5199  if(LeadElement == SecondNamedElementPos)
5200  {
5201  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5202  FrontTrainRearPosition = SecondNamedElementPos;
5203  RearTrainFrontPosition = FirstNamedElementPos;
5204  RearTrainRearPosition = FirstNamedLinkedElementPos;
5205  }
5206  else // LeadElement must == FirstNamedLinkedElementPos
5207  {
5208  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5209  FrontTrainRearPosition = FirstNamedElementPos;
5210  RearTrainFrontPosition = SecondNamedElementPos;
5211  RearTrainRearPosition = SecondNamedLinkedElementPos;
5212  }
5213  }
5214  RearTrainExitPos = -1;
5215  for(int x = 0; x < 4; x++)
5216  {
5217  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5218  {
5219  RearTrainExitPos = x;
5220  break;
5221  }
5222  }
5223  if(RearTrainExitPos == -1)
5224  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5225 
5226  FrontTrainExitPos = -1;
5227  for(int x = 0; x < 4; x++)
5228  {
5229  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5230  {
5231  FrontTrainExitPos = x;
5232  break;
5233  }
5234  }
5235  if(FrontTrainExitPos == -1)
5236  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5237 
5238  // check no train (apart from self) on any of the 4 elements & fail if so
5239  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5240  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5241 
5242  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5243  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5244  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5245  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5246  else
5247  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5248  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5249  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5250  // can't be a bridge
5251  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5252  // can't be a bridge
5253  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5254  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5255 
5256  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5257  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5258  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5259  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5260  else
5261  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5262 
5263  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5264  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5265  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5266  {
5268  {
5271  }
5272  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5273  Utilities->CallLogPop(1010);
5274  return;
5275  }
5276 
5278  {
5280  }
5281  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5282  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5283  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5284  // variable as it is needed for setting up the new train
5285  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5286 
5287  UnplotTrain(0);
5288  StartSpeed = 0;
5289  RearStartElement = RearTrainRearPosition;
5290  RearStartExitPos = RearTrainExitPos;
5291  StoppedAtLocation = true;
5292  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5293  {
5294  StoppedWithoutPower = true;
5295  }
5296  PlotStartPosition(3);
5301 
5302  Mass = Mass / 2;
5303  MaxBrakeRate = MaxBrakeRate / 2;
5304  PowerAtRail = PowerAtRail / 2;
5305  AValue = sqrt(2 * PowerAtRail / Mass);
5306  // shouldn't change but include in case not set earlier
5307 
5308  // create new front train
5309 /*
5310  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5311  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5312  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5313 */
5314  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5315  TActionEventType EventType = NoEvent;
5316 
5317  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5318  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5319  // false for SignallerControl
5320  {
5321  Utilities->CallLogPop(1721); // EventType not used here
5322  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5323  // another train, in which case a message will have been sent to the perf log, also might well clear later
5324  // when other train moves away
5325  return;
5326  }
5327  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5328  // see mods in UpdateTrain for v1.3.2
5329  TrainController->TrainAdded = true;
5330 
5331  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5332 
5333  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5334  TTOD.RunningEntry = Running;
5335  Utilities->CallLogPop(998);
5336 }
5337 
5338 // ---------------------------------------------------------------------------
5339 
5340 void TTrain::RearTrainSplit(int Caller)
5341 {
5342 /*
5343  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5344  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5345  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5346  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5347 */
5348  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5349  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5350  if(PowerAtRail < 1)
5351  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5352  {
5354  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5356  Utilities->CallLogPop(2138);
5357  return;
5358  }
5359  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5360 
5361  if(LocationName == "")
5362  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5363  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5364  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5365  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5367 
5368  // determine all positions & exits
5369  if(LocationName != "")
5370  {
5371  // if message given only call at ~5 sec intervals
5373  {
5374  FirstNamedElementPos = LeadElement;
5375  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5376  SecondNamedLinkedElementPos))
5377  {
5378  FirstNamedElementPos = MidElement;
5379  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5380  SecondNamedLinkedElementPos))
5381  {
5383  {
5384  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5386  }
5387  Utilities->CallLogPop(1013);
5388  return;
5389  }
5390  }
5391  else
5392  {
5393  // if first is possible then check if all 4 positions at location, and if not try the second
5394  int LeadPosA = FirstNamedElementPos;
5395  int LeadPosB = FirstNamedLinkedElementPos;
5396  int LeadPosC = SecondNamedElementPos;
5397  int LeadPosD = SecondNamedLinkedElementPos;
5398  // count number of positions that are at the location
5399  int LeadNumAtLoc = 0;
5400  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5401  LeadNumAtLoc++;
5402  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5403  LeadNumAtLoc++;
5404  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5405  LeadNumAtLoc++;
5406  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5407  LeadNumAtLoc++;
5408  if(LeadNumAtLoc < 4)
5409  {
5410  FirstNamedElementPos = MidElement;
5411  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5412  SecondNamedLinkedElementPos)) // restore originals
5413  {
5414  FirstNamedElementPos = LeadPosA;
5415  FirstNamedLinkedElementPos = LeadPosB;
5416  SecondNamedElementPos = LeadPosC;
5417  SecondNamedLinkedElementPos = LeadPosD;
5418  }
5419  else // count number at location
5420  {
5421  int MidNumAtLoc = 0;
5422  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5423  MidNumAtLoc++;
5424  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5425  MidNumAtLoc++;
5426  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5427  MidNumAtLoc++;
5428  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5429  MidNumAtLoc++;
5430  if(LeadNumAtLoc > MidNumAtLoc)
5431  // change back, else keep new values
5432  {
5433  FirstNamedElementPos = LeadPosA;
5434  FirstNamedLinkedElementPos = LeadPosB;
5435  SecondNamedElementPos = LeadPosC;
5436  SecondNamedLinkedElementPos = LeadPosD;
5437  }
5438  }
5439  }
5440  }
5441  }
5442  else
5443  {
5444  Utilities->CallLogPop(1792);
5445  return;
5446  }
5447  }
5448  else
5449  throw Exception("Error - LocationName not set in RearTrainSplit");
5450  // set front & rear train parameters
5451  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5452  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5453  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5454  if(LeadElement == FirstNamedElementPos)
5455  {
5456  if(MidElement == SecondNamedElementPos)
5457  {
5458  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5459  FrontTrainRearPosition = FirstNamedElementPos;
5460  RearTrainFrontPosition = SecondNamedElementPos;
5461  RearTrainRearPosition = SecondNamedLinkedElementPos;
5462  }
5463  else // MidElement must == FirstNamedLinkedElementPos
5464  {
5465  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5466  FrontTrainRearPosition = SecondNamedElementPos;
5467  RearTrainFrontPosition = FirstNamedElementPos;
5468  RearTrainRearPosition = FirstNamedLinkedElementPos;
5469  }
5470  }
5471  else // MidElement == FirstNamedElementPos
5472  {
5473  if(LeadElement == SecondNamedElementPos)
5474  {
5475  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5476  FrontTrainRearPosition = SecondNamedElementPos;
5477  RearTrainFrontPosition = FirstNamedElementPos;
5478  RearTrainRearPosition = FirstNamedLinkedElementPos;
5479  }
5480  else // LeadElement must == FirstNamedLinkedElementPos
5481  {
5482  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5483  FrontTrainRearPosition = FirstNamedElementPos;
5484  RearTrainFrontPosition = SecondNamedElementPos;
5485  RearTrainRearPosition = SecondNamedLinkedElementPos;
5486  }
5487  }
5488  RearTrainExitPos = -1;
5489  for(int x = 0; x < 4; x++)
5490  {
5491  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5492  {
5493  RearTrainExitPos = x;
5494  break;
5495  }
5496  }
5497  if(RearTrainExitPos == -1)
5498  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5499  FrontTrainExitPos = -1;
5500  for(int x = 0; x < 4; x++)
5501  {
5502  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5503  {
5504  FrontTrainExitPos = x;
5505  break;
5506  }
5507  }
5508  if(FrontTrainExitPos == -1)
5509  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5510 
5511  // check no train (apart from self) on any of the 4 elements & fail if so
5512  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5513  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5514 
5515  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5516  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5517  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5518  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5519  else
5520  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5521  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5522  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5523  // can't be a bridge
5524  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5525  // can't be a bridge
5526  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5527  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5528 
5529  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5530  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5531  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5532  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5533  else
5534  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5535 
5536  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5537  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5538  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5539  {
5541  {
5544  }
5545  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5546  Utilities->CallLogPop(1014);
5547  return;
5548  }
5549 
5551  {
5553  }
5554 
5555  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
5556  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5557  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5558  // variable as it is needed for setting up the new train
5559  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5560 
5561  UnplotTrain(1);
5562  StartSpeed = 0;
5563  RearStartElement = FrontTrainRearPosition;
5564  RearStartExitPos = FrontTrainExitPos;
5565  StoppedAtLocation = true;
5566  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5567  {
5568  StoppedWithoutPower = true;
5569  }
5570  PlotStartPosition(4);
5575  Mass = Mass / 2;
5576  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5577  MaxBrakeRate = MaxBrakeRate / 2;
5578  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5579  PowerAtRail = PowerAtRail / 2;
5580  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5581  AValue = sqrt(2 * PowerAtRail / Mass);
5582  // shouldn't change but include in case not set earlier
5583 
5584  // create new rear train
5585 /*
5586  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5587  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5588  int RepeatNumber, int IncrementalMinutes)
5589 */
5590  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5591  TActionEventType EventType = NoEvent;
5592 
5593  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5594  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5595  // false for SignallerControl
5596  {
5597  Utilities->CallLogPop(1722); // EventType not used here
5598  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5599  // another train, in which case a message will have been sent to the perf log, also might well clear later
5600  // when other train moves away
5601  return;
5602  }
5603  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5604  // see mods in UpdateTrain for v1.3.2
5605  TrainController->TrainAdded = true;
5606  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5607 
5608  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5609  TTOD.RunningEntry = Running;
5610  Utilities->CallLogPop(1015);
5611 }
5612 
5613 // ---------------------------------------------------------------------------
5614 
5615 void TTrain::FinishJoin(int Caller)
5616 {
5617  if(FinishJoinLogSent == false)
5618  {
5619  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5620  FinishJoinLogSent = true; // so don't keep logging this event
5621  // don't need to reset it to false after the event as the train is deleted
5622  }
5623  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5624  if(TrainFailed)
5625  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
5626  {
5628  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
5630  Utilities->CallLogPop(2139);
5631  return;
5632  }
5633  if(TrainGone)
5634  // this means that the train has already joined the other & is awaiting deletion by TrainController
5635  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
5636  // on from jbo & TrainToJoinIsAdjacent returns false
5637  {
5638  Utilities->CallLogPop(1035);
5639  return;
5640  }
5641  TTrain *TrainToJoin;
5643 
5644  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
5645  {
5647  {
5648  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
5651  }
5652  Utilities->CallLogPop(1030);
5653  return; // keep this here in case need to add code before final return
5654  }
5655  // no need to clear error report flag here, cleared in jbo function
5656  // No need to set TimetableFinished, done in jbo function
5657  Utilities->CallLogPop(1031);
5658 }
5659 
5660 // ---------------------------------------------------------------------------
5661 
5662 void TTrain::JoinedBy(int Caller)
5663 {
5664  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
5665  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
5666  if(PowerAtRail < 1)
5667  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
5668  {
5670  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
5672  Utilities->CallLogPop(2140);
5673  return;
5674  }
5675  TTrain *TrainToBeJoinedBy;
5677 
5678  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
5679  {
5681  {
5682  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
5685  }
5686  LastActionDelayFlag = true;
5687  // need to update LastActionTime if this train first to arrive as need 30s after
5688  // both adjacent before the join
5689  Utilities->CallLogPop(1032);
5690  return;
5691  }
5692  // here when other train is adjacent
5694  {
5696  // need to update this as need 30s after both adjacent before the join
5697  LastActionDelayFlag = false;
5698  Utilities->CallLogPop(1033);
5699  return;
5700  }
5701  // here when other train is adjacent & 30 secs elapsed since both adjacent
5702 
5703  // set new values for mass etc
5704  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
5705  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
5706  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
5707  double OwnBrakeForce = MaxBrakeRate * Mass;
5708  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
5709 
5710  Mass += TrainToBeJoinedBy->Mass;
5711  MaxBrakeRate = CombinedBrakeRate;
5712  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
5713  AValue = sqrt(2 * PowerAtRail / Mass);
5714 
5716  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
5717  TrainToBeJoinedBy->TimetableFinished = true;
5718  TrainToBeJoinedBy->TrainGone = true;
5719  // this will cause other train to be deleted
5720  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
5724  Utilities->CallLogPop(1034);
5725 }
5726 
5727 // ---------------------------------------------------------------------------
5728 
5730 {
5731  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
5732  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
5733  if(PowerAtRail < 1)
5734  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
5735  {
5737  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
5738  ZeroPowerNoCDTMessage = true;
5739  Utilities->CallLogPop(2141);
5740  return;
5741  }
5742  TColor TempColour = BackgroundColour;
5743 
5744  UnplotTrain(2);
5747  StartSpeed = 0;
5748  StoppedAtLocation = true;
5749  PlotStartPosition(1);
5750  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
5751  // plot same as was - should always be pale green
5755 
5756  //now erase a stub route if there is one, added at v2.5.1
5757  //first element of route is now immediately behind the train (i.e. next to MidElement)
5758  if(MidEntryPos >= 0)
5759  {
5760  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
5761  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
5762  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
5763  int RouteNumber = -1;
5764  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
5765  if(RouteType == TAllRoutes::NotAutoSigsRoute)
5766  {
5767  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
5768  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
5769  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation))//all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
5770  {
5771  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
5772  {
5773  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
5774  int TVPos2 = PDE.GetTrackVectorPosition();
5775  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
5777  {
5778  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
5779  }
5780  else
5781  {
5782  break;
5783  }
5784  }
5785  AllRoutes->RebuildRailwayFlag = true;
5786  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
5787  }
5788  }
5789  }
5790  Utilities->CallLogPop(1012);
5791 }
5792 
5793 // ---------------------------------------------------------------------------
5794 
5795 void TTrain::NewTrainService(int Caller)
5796  // change to new train, give new service message
5797 {
5798  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
5799  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
5800  if(PowerAtRail < 1)
5801  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
5802  {
5804  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
5806  Utilities->CallLogPop(2142);
5807  return;
5808  }
5810 
5812  UnplotTrain(3);
5815  StartSpeed = 0;
5820  HeadCode = NewHeadCode;
5821  StoppedAtLocation = true;
5822  PlotStartPosition(5);
5824  // pale green
5827  TerminatedMessageSent = false;
5828  Utilities->CallLogPop(1022);
5829 }
5830 
5831 // ---------------------------------------------------------------------------
5832 
5833 void TTrain::RemainHere(int Caller)
5834 {
5835  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
5836  if(RemainHereLogNotSent) // to prevent repeated logs
5837  {
5838  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
5839  RemainHereLogNotSent = false;
5840  }
5842  {
5843  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
5846  TerminatedMessageSent = true;
5847  }
5848  TimetableFinished = true;
5849  Utilities->CallLogPop(1023);
5850 }
5851 
5852 // ---------------------------------------------------------------------------
5853 
5854 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
5855 /*
5856  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
5857  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
5858  except where an action is a departure, starting at the current value for the pointer
5859  If IncNum is -1, then send messages for all remaining actions, including Fer if present
5860  If IncNum is -2, then send messages for all remaining actions, except Fer if present
5861 */ {
5862  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
5863  return; // if remove train that starts under signaller control no messages needed
5864 
5865  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
5866  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
5867  if(IncNum > 0)
5868  {
5869  for(int x = 0; x < IncNum; x++)
5870  {
5871  if(x > 0)
5872  Ptr++;
5873  // arrival - no need to test for termination as this section only covers missed actions up to the
5874  // arrival point - may terminate later but that not missed
5875  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
5876  {
5878  }
5879  // arrival & departure
5880  if(Ptr->FormatType == TimeTimeLoc)
5881  {
5883  }
5884  // departure
5885  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
5886  {
5887  continue; // skip TimeLoc departures, message given for arrivals
5888  }
5889  // pass
5890  else if(Ptr->FormatType == PassTime)
5891  {
5893  }
5894  // split
5895  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
5896  {
5898  }
5899  // jbo
5900  else if(Ptr->Command == "jbo")
5901  {
5903  }
5904  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
5905  // be starts, finishes or cdt
5906  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
5907  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
5908  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
5909  (Ptr->FormatType == Repeat))
5910  {
5911  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
5912  }
5913  }
5914  }
5915  else
5916  {
5917  bool IncludeFER = false;
5918  if(IncNum == -1)
5919  IncludeFER = true;
5920  while(true) // finish commands & repeats break out of loop
5921  {
5922  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
5923  if(!IncludeFER && (Ptr->Command == "Fer"))
5924  {
5925  break;
5926  }
5927  // Fer & included
5928  else if(IncludeFER && (Ptr->Command == "Fer"))
5929  {
5931  break;
5932  }
5933  // Repeat
5934  else if(Ptr->FormatType == Repeat)
5935  {
5936  break;
5937  }
5938  // Fjo
5939  else if(Ptr->Command == "Fjo")
5940  {
5942  break;
5943  }
5944  // Frh
5945  else if(Ptr->Command == "Frh")
5946  {
5948  {
5950  TerminatedMessageSent = true;
5951  }
5952  break;
5953  }
5954  // Frh-sh
5955  else if(Ptr->Command == "Frh-sh")
5956  {
5958  {
5960  TerminatedMessageSent = true;
5961  }
5962  break;
5963  }
5964  // Fns, F-nshs, Fns-sh
5965  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
5966  {
5968  break;
5969  }
5970  // end of breakout actions
5971  // arrival
5972  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
5973  {
5974  if(IsTrainTerminating(1))
5975  {
5977  TerminatedMessageSent = true;
5978  }
5979  else
5980  {
5982  }
5983  }
5984  // arrival & departure
5985  else if(Ptr->FormatType == TimeTimeLoc)
5986  {
5988  }
5989  // departure
5990  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
5991  {
5992  Ptr++;
5993  continue; // skip TimeLoc departures, message given for arrivals
5994  }
5995  // pass
5996  else if(Ptr->FormatType == PassTime)
5997  {
5999  }
6000  // split
6001  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6002  {
6004  }
6005  // jbo
6006  else if(Ptr->Command == "jbo")
6007  {
6009  }
6010  // cdt
6011  else if(Ptr->Command == "cdt")
6012  {
6014  }
6015  // Errors
6016  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6017  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6018  {
6019  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6020  }
6021  Ptr++;
6022  }
6023  TimetableFinished = true;
6024  }
6025  Utilities->CallLogPop(1021);
6026 }
6027 
6028 // ---------------------------------------------------------------------------
6029 
6030 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6031  // ensure same repeatnumber
6032 {
6033  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6035 
6036  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6037  {
6038  Utilities->CallLogPop(1024);
6039  return false;
6040  }
6041  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6042  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6043  {
6044  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6045  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6046  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6047  {
6048  Utilities->CallLogPop(1025);
6049  return true;
6050  }
6051  }
6052  Utilities->CallLogPop(1026);
6053  return false;
6054 }
6055 
6056 // ---------------------------------------------------------------------------
6057 
6058 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6059  // ensure same repeatnumber
6060 {
6061  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6062  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6063 
6064  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6065  {
6066  Utilities->CallLogPop(1027);
6067  return false;
6068  }
6069  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6070  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6071  {
6072  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6073  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6074  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6075  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6076  {
6077  Utilities->CallLogPop(1028);
6078  return true;
6079  }
6080  }
6081  Utilities->CallLogPop(1029);
6082  return false;
6083 }
6084 
6085 // ---------------------------------------------------------------------------
6086 
6088 {
6089  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6090  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6091  if(PowerAtRail < 1)
6092  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6093  {
6095  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6097  Utilities->CallLogPop(2143);
6098  return;
6099  }
6100  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6101 
6103  UnplotTrain(4);
6106  StartSpeed = 0;
6111  HeadCode = NewHeadCode;
6112  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6113  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6114  StoppedAtLocation = true;
6115  PlotStartPosition(6);
6117  // pale green
6120  TerminatedMessageSent = false;
6121  Utilities->CallLogPop(1078);
6122 }
6123 
6124 // ---------------------------------------------------------------------------
6125 
6127  // need to check whether all repeats finished or not
6128 {
6129  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6130  if(RemainHereLogNotSent) // to prevent repeated logs
6131  {
6132  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6133  RemainHereLogNotSent = false;
6134  }
6136  // finished all repeats
6137  {
6139  {
6142  TerminatedMessageSent = true;
6143  // no need to clear message as no more actions
6144  }
6145  TimetableFinished = true;
6146  Utilities->CallLogPop(1080);
6147  return;
6148  }
6149  if(PowerAtRail < 1)
6150  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6151  {
6153  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6155  Utilities->CallLogPop(2144);
6156  return;
6157  }
6158  int TempRepeatNumber = RepeatNumber + 1;
6159  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6160  // until after LogAction or the wrong time will be used
6161  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6162 
6164  RepeatNumber++;
6165  UnplotTrain(5);
6168  StartSpeed = 0;
6173  HeadCode = NewHeadCode;
6174  StoppedAtLocation = true;
6175  PlotStartPosition(7);
6177  // pale green
6180  TerminatedMessageSent = false;
6181  Utilities->CallLogPop(1079);
6182 }
6183 
6184 // ---------------------------------------------------------------------------
6185 
6187 {
6188  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6189  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6190  if(PowerAtRail < 1)
6191  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6192  {
6194  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6196  Utilities->CallLogPop(2145);
6197  return;
6198  }
6200  // finished all repeats
6201  {
6202  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6204  RepeatNumber = 0;
6205  UnplotTrain(6);
6208  StartSpeed = 0;
6210  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6211  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6213  HeadCode = NewHeadCode;
6214  StoppedAtLocation = true;
6215  PlotStartPosition(9);
6219  TerminatedMessageSent = false;
6220  Utilities->CallLogPop(1081);
6221  return;
6222  }
6223  int TempRepeatNumber = RepeatNumber + 1;
6224  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6225  // until after LogAction or the wrong time will be used
6226  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6227 
6229  RepeatNumber++;
6230  UnplotTrain(7);
6233  StartSpeed = 0;
6238  HeadCode = NewHeadCode;
6239  StoppedAtLocation = true;
6240  PlotStartPosition(8);
6242  // pale green
6245  TerminatedMessageSent = false;
6246  Utilities->CallLogPop(1082);
6247 }
6248 
6249 // ---------------------------------------------------------------------------
6250 
6252 {
6253  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6254  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6255  // must be preceded by a TimeLoc departure
6256  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6257  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6258  {
6260  {
6261  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6262  {
6263  Utilities->CallLogPop(1083);
6264  return false;
6265  }
6266  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6267  {
6268  Utilities->CallLogPop(1084);
6269  return true;
6270  }
6271  }
6272  }
6273  Utilities->CallLogPop(1085);
6274  return false;
6275 }
6276 
6277 // ---------------------------------------------------------------------------
6278 
6279 bool TTrain::AbleToMove(int Caller)
6280 {
6281  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6282  bool Able = true;
6283 
6284  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6285  { // StoppedForTrainInFront removed as tested below
6286  Able = false;
6287  Utilities->CallLogPop(2146); // added v2.4.0
6288  return Able; // added v2.4.0
6289  }
6290  if(LeadElement > -1)
6291  {
6292  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6293  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6294  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6295  {
6296  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6297  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6298  {
6299  Able = true;
6300  StoppedForTrainInFront = false;
6301  }
6302  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6303  {
6304  Able = true;
6305  StoppedForTrainInFront = false;
6306  }
6307  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6308  {
6309  Able = true;
6310  StoppedForTrainInFront = false;
6311  }
6312  }
6313  else
6314  {
6316  Able = false;
6317  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6318  }
6319  }
6320  else // leaving at a continuation so keep going
6321  {
6322  Able = true;
6323  StoppedForTrainInFront = false;
6324  }
6325  Utilities->CallLogPop(1454);
6326  return Able;
6327 }
6328 
6329 // ---------------------------------------------------------------------------
6330 
6332 {
6333  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6334  // won't be set; if there is a train then set StoppedForTrainInFront
6335  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6336  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6337  if(LeadElement == -1) // exiting at continuation
6338  {
6339  Utilities->CallLogPop(2045);
6340  return false;
6341  }
6342  // end of addition
6343  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6344  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6345 
6346  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6347  {
6348  StoppedForTrainInFront = true;
6349  Utilities->CallLogPop(1455);
6350  return false;
6351  }
6352  else
6353  {
6354  Utilities->CallLogPop(1456);
6356  // StoppedWithoutPower added v2.4.0
6357  }
6358 }
6359 
6360 // ---------------------------------------------------------------------------
6361 
6363 {
6364  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6365  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6366  TColor TempColour = BackgroundColour;
6367 
6368  UnplotTrain(8);
6371  StartSpeed = 0;
6372  PlotStartPosition(2);
6373  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6374 
6375  //now erase a stub route if there is one, added at v2.5.1
6376  //first element of route is now immediately behind the train (i.e. next to MidElement)
6377  if(MidEntryPos >= 0)
6378  {
6379  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6380  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6381  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6382  int RouteNumber = -1;
6383  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6384  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6385  {
6386  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6387  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6388  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation))//all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6389  {
6390  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6391  {
6392  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6393  int TVPos2 = PDE.GetTrackVectorPosition();
6394  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6396  {
6397  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6398  }
6399  else
6400  {
6401  break;
6402  }
6403  }
6404  AllRoutes->RebuildRailwayFlag = true;
6405  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6406  }
6407  }
6408  }
6409  Utilities->CallLogPop(1102);
6410 }
6411 
6412 // ---------------------------------------------------------------------------
6413 
6415 {
6416  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6417  ",FloatingLabelNextString" + "," + HeadCode);
6418  AnsiString RetStr = "", LocationName = "";
6419 
6420  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6421  {
6422  throw Exception("Error - start entry in FloatingLabelNextString");
6423  }
6424  if(Ptr->FormatType == TimeTimeLoc)
6425  {
6426  if(TrainMode == Timetable)
6427  {
6428  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6429  // not arrived yet in tt mode
6430  {
6431  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6432  }
6433  else
6434  {
6435  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6436  }
6437  }
6438  else // TrainMode == Signaller
6439  {
6440  if(!DepartureTimeSet) // not arrived yet
6441  {
6442  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6443  }
6444  else
6445  {
6446  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6447  }
6448  }
6449  }
6450  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6451  {
6452  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6453  }
6454  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6455  {
6456  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6457  }
6458  else if(Ptr->FormatType == PassTime) // new
6459  {
6460  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6461  }
6462  else if(Ptr->Command == "Fns")
6463  {
6464  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6465  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6466  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6467  }
6468  else if(Ptr->Command == "F-nshs")
6469  {
6470  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6472  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6473  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6474  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6475  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6476  }
6477  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6478  {
6479  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6480  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6481  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6482  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6483  }
6484  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6485  {
6486  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6487  + " at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6488  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6489  }
6490  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6491  {
6492  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6493  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6494  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6495  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6496  }
6497  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6498  {
6499  RetStr ="None, train terminated at " + Ptr->LocationName;
6500  }
6501  else if(Ptr->Command == "Frh")
6502  {
6503  RetStr = "None, train terminated at " + Ptr->LocationName;
6504  }
6505  else if(Ptr->Command == "Fer")
6506  {
6507  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime));
6508  }
6509  else if(Ptr->Command == "Fjo")
6510  {
6511  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
6513  }
6514  else if(Ptr->Command == "jbo")
6515  {
6516  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6517  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
6518  }
6519  else if(Ptr->Command == "fsp")
6520  {
6521  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6522  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
6523  }
6524  else if(Ptr->Command == "rsp")
6525  {
6526  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6527  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
6528  }
6529  else if(Ptr->Command == "cdt")
6530  {
6531  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
6532  }
6533  Utilities->CallLogPop(1124);
6534  return RetStr;
6535 }
6536 
6537 // ---------------------------------------------------------------------------
6538 
6539 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
6540 {
6541  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
6542  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
6543  AnsiString DepTime = "", EventTime = "";
6544  bool CDTFlag = false; //reports if train changes direction before departs
6545  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
6546  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
6547  {
6548  if(AVI->Command == "cdt")
6549  {
6550  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
6551  continue;
6552  }
6553  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
6554  {
6555  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
6556  RetStr += "\nNew service splits at " + EventTime;
6557  Utilities->CallLogPop(2234);
6558  return RetStr;
6559  }
6560  if(AVI->Command == "jbo")
6561  {
6562  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
6563  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
6564  Utilities->CallLogPop(2235);
6565  return RetStr;
6566  }
6567  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
6568  {
6569  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
6570  if(CDTFlag)
6571  {
6572  RetStr += "\nNew service changes direction then departs at " + DepTime;
6573  }
6574  else
6575  {
6576  RetStr += "\nNew service departs at " + DepTime;
6577  }
6578  Utilities->CallLogPop(2236);
6579  return RetStr;
6580  }
6581  }
6582  Utilities->CallLogPop(2208);
6583  return RetStr; //if reach here then RetStr doesn't change
6584 }
6585 
6586 // ---------------------------------------------------------------------------
6587 
6589  // Enter with Ptr pointing to first action to be listed (i.e. next action)
6590 {
6591  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6592  ",FloatingTimetableString" + "," + HeadCode);
6593  AnsiString RetStr = "", PartStr = "";
6594  int Count = 0;
6595 
6596  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
6597  // can start in signaller control so exclude this
6598  {
6599  throw Exception("Error - start entry in FloatingTimetableString");
6600  }
6601  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTime Loc check later
6602  bool FirstPass = true;
6603  Ptr--; // because incremented at start of loop
6604 
6605  // different first TimeTimeLoc display if in signaller control
6606  do
6607  {
6608  Ptr++;
6609  if((Ptr->FormatType == Repeat) || TimetableFinished)
6610  break;
6611  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
6612  {
6613  AnsiString TrainLoc = "";
6614  if(TrainMode == Timetable)
6615  {
6616  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
6617  {
6618  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6619  }
6620  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6621  {
6622  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6623  }
6624  else
6625  {
6626  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6627  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6628  Count++; // because there are 2 entries
6629  }
6630  }
6631  else // TrainMode == Signaller
6632  {
6633  if(DepartureTimeSet)
6634  {
6635  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6636  }
6637  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6638  {
6639  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6640  }
6641  else
6642  {
6643  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6644  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6645  Count++; // because there are 2 entries
6646  }
6647  }
6648  }
6649  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
6650  {
6651  AnsiString TrainLoc = "";
6652  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
6653  {
6654  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6655  }
6656  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6657  {
6658  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6659  }
6660  else
6661  {
6662  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6663  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6664  Count++; // because there are 2 entries
6665  }
6666  }
6667  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6668  {
6669  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
6670  }
6671  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6672  {
6673  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6674  }
6675  else if(Ptr->FormatType == PassTime) // new
6676  {
6677  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
6678  }
6679  else if(Ptr->Command == "Fns")
6680  {
6681  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
6682  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6683  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
6684  }
6685  else if(Ptr->Command == "F-nshs")
6686  {
6687  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
6688  Ptr->LocationName;
6689  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6690  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6691  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6692  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6693  }
6694  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
6695  {
6696  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
6697  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
6698  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
6699  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6700  }
6701  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6702  {
6703  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6704  + " at " + Ptr->LocationName;
6705  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6706  }
6707  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
6708  {
6709  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
6710  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
6711  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
6712  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6713  }
6714  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6715  {
6716  PartStr = "Terminate at " + Ptr->LocationName;
6717  }
6718  else if(Ptr->Command == "Frh")
6719  {
6720  PartStr = "Terminate at " + Ptr->LocationName;
6721  }
6722  else if(Ptr->Command == "Fer")
6723  {
6724  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList);
6725  }
6726  else if(Ptr->Command == "Fjo")
6727  {
6728  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
6729  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6730  }
6731  else if(Ptr->Command == "jbo")
6732  {
6733  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
6734  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6735  }
6736  else if(Ptr->Command == "fsp")
6737  {
6738  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
6739  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6740  }
6741  else if(Ptr->Command == "rsp")
6742  {
6743  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
6744  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6745  }
6746  else if(Ptr->Command == "cdt")
6747  {
6748  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
6749  }
6750  if(RetStr != "")
6751  RetStr = RetStr + '\n' + PartStr;
6752  else
6753  RetStr = PartStr;
6754  FirstPass = false;
6755  Count++;
6756  }
6757  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
6758  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
6759  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
6760  // forward as anyone should wish to see without looking at the full timetable
6761  if(TimetableFinished)
6762  {
6763  if(TrainMode == Timetable)
6764  RetStr = "Timetable finished";
6765  else
6766  RetStr = "No timetable";
6767  }
6768  Utilities->CallLogPop(1125);
6769  return RetStr;
6770 }
6771 
6772 // ---------------------------------------------------------------------------
6773 
6774 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
6775 {
6776  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
6777  Utilities->SaveFileString(OutFile, HeadCode);
6780  Utilities->SaveFileInt(OutFile, StartSpeed);
6783  Utilities->SaveFileInt(OutFile, RepeatNumber);
6786  Utilities->SaveFileInt(OutFile, Mass);
6789  Utilities->SaveFileDouble(OutFile, EntrySpeed);
6796  Utilities->SaveFileDouble(OutFile, BrakeRate);
6800  Utilities->SaveFileDouble(OutFile, double(EntryTime));
6801  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
6802  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
6803  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
6804  Utilities->SaveFileDouble(OutFile, double(TRSTime));
6805  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
6809  Utilities->SaveFileInt(OutFile, (short)TrainMode);
6814  Utilities->SaveFileBool(OutFile, Derailed);
6816  Utilities->SaveFileBool(OutFile, Crashed);
6823  Utilities->SaveFileBool(OutFile, NotInService);
6824  Utilities->SaveFileBool(OutFile, Plotted);
6825  Utilities->SaveFileBool(OutFile, TrainGone);
6826  Utilities->SaveFileBool(OutFile, SPADFlag);
6828  Utilities->SaveFileInt(OutFile, HOffset[0]);
6829  Utilities->SaveFileInt(OutFile, HOffset[1]);
6830  Utilities->SaveFileInt(OutFile, HOffset[2]);
6831  Utilities->SaveFileInt(OutFile, HOffset[3]);
6832  Utilities->SaveFileInt(OutFile, VOffset[0]);
6833  Utilities->SaveFileInt(OutFile, VOffset[1]);
6834  Utilities->SaveFileInt(OutFile, VOffset[2]);
6835  Utilities->SaveFileInt(OutFile, VOffset[3]);
6836  Utilities->SaveFileInt(OutFile, PlotElement[0]);
6837  Utilities->SaveFileInt(OutFile, PlotElement[1]);
6838  Utilities->SaveFileInt(OutFile, PlotElement[2]);
6839  Utilities->SaveFileInt(OutFile, PlotElement[3]);
6840  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
6841  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
6842  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
6843  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
6845  Utilities->SaveFileInt(OutFile, (short)Straddle);
6846  Utilities->SaveFileInt(OutFile, NextTrainID);
6847  Utilities->SaveFileInt(OutFile, TrainID);
6848  Utilities->SaveFileInt(OutFile, LeadElement);
6849  Utilities->SaveFileInt(OutFile, LeadEntryPos);
6850  Utilities->SaveFileInt(OutFile, LeadExitPos);
6851  Utilities->SaveFileInt(OutFile, MidElement);
6852  Utilities->SaveFileInt(OutFile, MidEntryPos);
6853  Utilities->SaveFileInt(OutFile, MidExitPos);
6854  Utilities->SaveFileInt(OutFile, LagElement);
6855  Utilities->SaveFileInt(OutFile, LagEntryPos);
6856  Utilities->SaveFileInt(OutFile, LagExitPos);
6857  int ColourNumber;
6858 
6860  ColourNumber = 0;
6862  ColourNumber = 1;
6864  ColourNumber = 2;
6866  ColourNumber = 3;
6868  ColourNumber = 4;
6870  ColourNumber = 5;
6872  ColourNumber = 6;
6874  ColourNumber = 7;
6876  ColourNumber = 8;
6878  ColourNumber = 9;
6880  ColourNumber = 10;
6882  ColourNumber = 11;
6884  ColourNumber = 12;
6885  else if(BackgroundColour == clTRSBackground)
6886  ColourNumber = 13;
6888  ColourNumber = 14; // added at v2.4.0
6889  Utilities->SaveFileInt(OutFile, ColourNumber);
6890 
6891  // additional data
6892  bool ForwardHeadCode;
6893 
6894  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
6895  {
6896  ForwardHeadCode = true;
6897  }
6898  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
6899  else
6900  {
6901  ForwardHeadCode = false;
6902  }
6903  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
6904 
6905  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
6906 
6907  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
6908  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
6909 
6910  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
6911  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
6912  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
6913  // so use the last asterisk position for this - 0 for false & 1 for true
6914  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
6915  AnsiString Marker;
6916 
6918  Marker = "*****1";
6919  else
6920  Marker = "*****0";
6921  if(RestoreTimetableLocation == "")
6922  Utilities->SaveFileString(OutFile, Marker);
6923  else
6924  {
6925  AnsiString CombinedString = RestoreTimetableLocation + Marker;
6926  Utilities->SaveFileString(OutFile, CombinedString);
6927  // RestoreTimetableLocation + marker
6928  }
6929  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
6930  Utilities->CallLogPop(1457);
6931 }
6932 
6933 // ---------------------------------------------------------------------------
6934 
6935 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
6936 {
6937  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
6938  HeadCode = Utilities->LoadFileString(InFile);
6941  StartSpeed = Utilities->LoadFileInt(InFile);
6943  if(SignallerMaxSpeed < 10)
6944  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
6946  RepeatNumber = Utilities->LoadFileInt(InFile);
6949  Mass = Utilities->LoadFileInt(InFile);
6953  // above added at v2.1.0 for legacy session files where value may not have been limited
6955  EntrySpeed = Utilities->LoadFileDouble(InFile);
6959  if(TimetableMaxRunningSpeed < 10)
6960  {
6961  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
6962  }
6964  if(MaxRunningSpeed < 10)
6965  {
6966  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
6967  }
6970  BrakeRate = Utilities->LoadFileDouble(InFile);
6974  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
6975  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
6976  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
6977  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
6978  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
6979  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
6988  Derailed = Utilities->LoadFileBool(InFile);
6990  Crashed = Utilities->LoadFileBool(InFile);
6997  NotInService = Utilities->LoadFileBool(InFile);
6998  Plotted = Utilities->LoadFileBool(InFile);
6999  TrainGone = Utilities->LoadFileBool(InFile);
7000  SPADFlag = Utilities->LoadFileBool(InFile);
7002  HOffset[0] = Utilities->LoadFileInt(InFile);
7003  HOffset[1] = Utilities->LoadFileInt(InFile);
7004  HOffset[2] = Utilities->LoadFileInt(InFile);
7005  HOffset[3] = Utilities->LoadFileInt(InFile);
7006  VOffset[0] = Utilities->LoadFileInt(InFile);
7007  VOffset[1] = Utilities->LoadFileInt(InFile);
7008  VOffset[2] = Utilities->LoadFileInt(InFile);
7009  VOffset[3] = Utilities->LoadFileInt(InFile);
7010  PlotElement[0] = Utilities->LoadFileInt(InFile);
7011  PlotElement[1] = Utilities->LoadFileInt(InFile);
7012  PlotElement[2] = Utilities->LoadFileInt(InFile);
7013  PlotElement[3] = Utilities->LoadFileInt(InFile);
7014  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7015  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7016  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7017  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7019  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7020  NextTrainID = Utilities->LoadFileInt(InFile);
7021  // will be same for all but best to save all anyway
7022  TrainID = Utilities->LoadFileInt(InFile);
7023  LeadElement = Utilities->LoadFileInt(InFile);
7024  LeadEntryPos = Utilities->LoadFileInt(InFile);
7025  LeadExitPos = Utilities->LoadFileInt(InFile);
7026  MidElement = Utilities->LoadFileInt(InFile);
7027  MidEntryPos = Utilities->LoadFileInt(InFile);
7028  MidExitPos = Utilities->LoadFileInt(InFile);
7029  LagElement = Utilities->LoadFileInt(InFile);
7030  LagEntryPos = Utilities->LoadFileInt(InFile);
7031  LagExitPos = Utilities->LoadFileInt(InFile);
7032  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7033 
7034  if(ColourNumber == 0)
7036  else if(ColourNumber == 1)
7038  else if(ColourNumber == 2)
7040  else if(ColourNumber == 3)
7042  else if(ColourNumber == 4)
7044  else if(ColourNumber == 5)
7046  else if(ColourNumber == 6)
7048  else if(ColourNumber == 7)
7050  else if(ColourNumber == 8)
7052  else if(ColourNumber == 9)
7054  else if(ColourNumber == 10)
7056  else if(ColourNumber == 11)
7058  else if(ColourNumber == 12)
7060  else if(ColourNumber == 13)
7062  else if(ColourNumber == 14)
7063  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7064 
7065  // additional data
7067  // sets the BackgroundColour to the loaded value
7068  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7069 
7070  if(ForwardHeadCode)
7071  {
7072  for(int x = 0; x < 4; x++)
7073  {
7075  }
7076  }
7077  else
7078  {
7079  for(int x = 0; x < 4; x++)
7080  {
7081  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7082  }
7083  }
7084 
7085  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7086  if(TrainMode == Timetable)
7087  {
7088  if(Crashed)
7090  else
7092  }
7093  else
7096  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7097  if(Straddle == LeadMid)
7098  {
7099  if(LeadElement > -1)
7101  if(LeadElement > -1)
7103  if(MidElement > -1)
7105  if(MidElement > -1)
7107  }
7108  else if(Straddle == LeadMidLag)
7109  {
7110  if(LeadElement > -1)
7112  if(MidElement > -1)
7114  if(MidElement > -1)
7116  if(LagElement > -1)
7118  }
7119 
7120  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7121 
7122  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7123  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7124 
7125  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7126 
7127  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7128  if(LeadElement > -1)
7129  // need to include this in case train exiting & no lead element
7130  {
7132  {
7133  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7134  }
7135  }
7136  AValue = sqrt(2 * PowerAtRail / Mass);
7137 
7138  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7139 
7140  // possible RestoreTimetableLocation + Marker, where Marker is
7141  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7142  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7143  // added at beta v0.2e
7144  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7145  // name not allowed to include the '*' character
7146  {
7147  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7148  bool GiveMessagesFalse = false;
7149  bool CheckLocationsExistInRailwayTrue = true;
7150  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7151  { // otherwise take no action
7152  RestoreTimetableLocation = Location;
7153  }
7154  }
7155  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7156 
7157  StoppedWithoutPower = false;
7158  if(Marker[6] == '1')
7159  {
7160  StoppedWithoutPower = true;
7161  }
7162  Utilities->CallLogPop(1458);
7163 }
7164 
7165 // ---------------------------------------------------------------------------
7166 
7167 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7168 {
7170  return false; // HeadCode
7171 
7172  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7173  return false; // RearStartElement
7174 
7175  if(!Utilities->CheckFileInt(InFile, 0, 3))
7176  return false; // RearStartExitPos
7177 
7178  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7179  return false; // StartSpeed
7180 
7181  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7182  return false; // SignallerMaxSpeed
7183 
7184  if(!Utilities->CheckFileBool(InFile))
7185  return false; // HoldAtLocationInTTMode
7186 
7187  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7188  return false; // RepeatNumber (max 96 x 60 at 1 min intervals)
7189 
7190  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7191  return false; // IncrementalMinutes (max 96 x 60)
7192 
7193  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7194  return false; // IncrementalDigits
7195 
7196  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7197  return false; // Mass
7198 
7199  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7200  return false;
7201 
7202  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7203  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7204  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7205  return false; // FrontElementLength
7206 
7207  if(!Utilities->CheckFileDouble(InFile))
7208  return false; // EntrySpeed
7209 
7210  if(!Utilities->CheckFileDouble(InFile))
7211  return false; // ExitSpeedHalf
7212 
7213  if(!Utilities->CheckFileDouble(InFile))
7214  return false; // ExitSpeedFull
7215 
7216  if(!Utilities->CheckFileDouble(InFile))
7217  return false; // TimetableMaxRunningSpeed
7218 
7219  if(!Utilities->CheckFileDouble(InFile))
7220  return false; // MaxRunningSpeed
7221 
7222  if(!Utilities->CheckFileDouble(InFile))
7223  return false; // MaxExitSpeed
7224 
7225  if(!Utilities->CheckFileDouble(InFile))
7226  return false; // MaxBrakeRate
7227 
7228  if(!Utilities->CheckFileDouble(InFile))
7229  return false; // BrakeRate
7230 
7231  if(!Utilities->CheckFileDouble(InFile))
7232  return false; // PowerAtRail
7233 
7234  if(!Utilities->CheckFileBool(InFile))
7235  return false; // FirstHalfMove
7236 
7237  if(!Utilities->CheckFileBool(InFile))
7238  return false; // OneLengthAccelDecel
7239 
7240  if(!Utilities->CheckFileDouble(InFile))
7241  return false; // double(EntryTime)
7242 
7243  if(!Utilities->CheckFileDouble(InFile))
7244  return false; // double(ExitTimeHalf)
7245 
7246  if(!Utilities->CheckFileDouble(InFile))
7247  return false; // double(ExitTimeFull)
7248 
7249  if(!Utilities->CheckFileDouble(InFile))
7250  return false; // double(ReleaseTime)
7251 
7252  if(!Utilities->CheckFileDouble(InFile))
7253  return false; // double(TRSTime)
7254 
7255  if(!Utilities->CheckFileDouble(InFile))
7256  return false; // double(LastActionTime)
7257 
7258  if(!Utilities->CheckFileBool(InFile))
7259  return false; // CallingOnFlag
7260 
7261  if(!Utilities->CheckFileBool(InFile))
7262  return false; // BeingCalledOn
7263 
7264  if(!Utilities->CheckFileBool(InFile))
7265  return false; // DepartureTimeSet
7266 
7267  if(!Utilities->CheckFileInt(InFile, 0, 2))
7268  return false; // (short)TrainMode
7269 
7270  if(!Utilities->CheckFileBool(InFile))
7271  return false; // TimetableFinished
7272 
7273  if(!Utilities->CheckFileBool(InFile))
7274  return false; // LastActionDelayFlag
7275 
7276  if(!Utilities->CheckFileBool(InFile))
7277  return false; // SignallerRemoved
7278 
7279  if(!Utilities->CheckFileBool(InFile))
7280  return false; // TerminatedMessageSent
7281 
7282  if(!Utilities->CheckFileBool(InFile))
7283  return false; // Derailed
7284 
7285  if(!Utilities->CheckFileBool(InFile))
7286  return false; // DerailPending
7287 
7288  if(!Utilities->CheckFileBool(InFile))
7289  return false; // Crashed
7290 
7291  if(!Utilities->CheckFileBool(InFile))
7292  return false; // StoppedAtBuffers
7293 
7294  if(!Utilities->CheckFileBool(InFile))
7295  return false; // StoppedAtSignal
7296 
7297  if(!Utilities->CheckFileBool(InFile))
7298  return false; // StoppedAtLocation
7299 
7300  if(!Utilities->CheckFileBool(InFile))
7301  return false; // SignallerStopped
7302 
7303  if(!Utilities->CheckFileBool(InFile))
7304  return false; // StoppedAfterSPAD
7305 
7306  if(!Utilities->CheckFileBool(InFile))
7307  return false; // StoppedForTrainInFront
7308 
7309  if(!Utilities->CheckFileBool(InFile))
7310  return false; // NotInService
7311 
7312  if(!Utilities->CheckFileBool(InFile))
7313  return false; // Plotted
7314 
7315  if(!Utilities->CheckFileBool(InFile))
7316  return false; // TrainGone
7317 
7318  if(!Utilities->CheckFileBool(InFile))
7319  return false; // SPADFlag
7320 
7321  if(!Utilities->CheckFileBool(InFile))
7322  return false; // TimeTimeLocArrived
7323 
7324  if(!Utilities->CheckFileInt(InFile, 0, 15))
7325  return false; // HOffset[0]
7326 
7327  if(!Utilities->CheckFileInt(InFile, 0, 15))
7328  return false; // HOffset[1]
7329 
7330  if(!Utilities->CheckFileInt(InFile, 0, 15))
7331  return false; // HOffset[2]
7332 
7333  if(!Utilities->CheckFileInt(InFile, 0, 15))
7334  return false; // HOffset[3]
7335 
7336  if(!Utilities->CheckFileInt(InFile, 0, 15))
7337  return false; // VOffset[0]
7338 
7339  if(!Utilities->CheckFileInt(InFile, 0, 15))
7340  return false; // VOffset[1]
7341 
7342  if(!Utilities->CheckFileInt(InFile, 0, 15))
7343  return false; // VOffset[2]
7344 
7345  if(!Utilities->CheckFileInt(InFile, 0, 15))
7346  return false; // VOffset[3]
7347 
7348  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7349  return false; // PlotElement[0]
7350 
7351  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7352  return false; // PlotElement[1]
7353 
7354  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7355  return false; // PlotElement[2]
7356 
7357  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7358  return false; // PlotElement[3]
7359 
7360  if(!Utilities->CheckFileInt(InFile, 0, 3))
7361  return false; // PlotEntryPos[0]
7362 
7363  if(!Utilities->CheckFileInt(InFile, 0, 3))
7364  return false; // PlotEntryPos[1]
7365 
7366  if(!Utilities->CheckFileInt(InFile, 0, 3))
7367  return false; // PlotEntryPos[2]
7368 
7369  if(!Utilities->CheckFileInt(InFile, 0, 3))
7370  return false; // PlotEntryPos[3]
7371 
7372  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7373  return false; // TrainCrashedInto
7374 
7375  if(!Utilities->CheckFileInt(InFile, 0, 2))
7376  return false; // (short)Straddle
7377 
7378  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7379  return false; // NextTrainID
7380 
7381  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7382  return false; // TrainID
7383 
7384  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7385  return false; // LeadElement
7386 
7387  if(!Utilities->CheckFileInt(InFile, 0, 3))
7388  return false; // LeadEntryPos
7389 
7390  if(!Utilities->CheckFileInt(InFile, 0, 3))
7391  return false; // LeadExitPos
7392 
7393  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7394  return false; // MidElement
7395 
7396  if(!Utilities->CheckFileInt(InFile, 0, 3))
7397  return false; // MidEntryPos
7398 
7399  if(!Utilities->CheckFileInt(InFile, 0, 3))
7400  return false; // MidExitPos
7401 
7402  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7403  return false; // LagElement
7404 
7405  if(!Utilities->CheckFileInt(InFile, 0, 3))
7406  return false; // LagEntryPos
7407 
7408  if(!Utilities->CheckFileInt(InFile, 0, 3))
7409  return false; // LagExitPos
7410 
7411  if(!Utilities->CheckFileInt(InFile, 0, 14))
7412  return false;
7413 
7414  // Background colour number //14 is failed colour at v2.4.0
7415  if(!Utilities->CheckFileBool(InFile))
7416  return false; // ForwardHeadCode
7417 
7418  if(!Utilities->CheckFileInt(InFile, 0, 10000))
7419  return false; // TrainDataEntryValue
7420 
7421  if(!Utilities->CheckFileInt(InFile, 0, 10000))
7422  return false; // ActionVectorEntryValue
7423 
7425  return false; // End of train marker + possible RestoreTimetableLocation
7426 
7427  // and StoppedWithoutPower flag
7428  return true;
7429 }
7430 
7431 // ---------------------------------------------------------------------------
7432 
7433 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
7434 {
7435  // order below reflects significance so earlier shows first, as may have more than one flag set
7436  // only plot flashing trains when Flash is true
7437 
7438 /*
7439  clCrashedBackground (TColor)0x0000FF red
7440  clDerailedBackground (TColor)0x0000FF red
7441  clSPADBackground (TColor)0x00FFFF yellow
7442  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
7443  clCallOnBackground (TColor)0xFF33FF light magenta
7444  clSignalStopBackground (TColor)0x00FF66 green
7445  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
7446  clStationStopBackground (TColor)0xCCFFCC pale green
7447  clTRSBackground (TColor)0xFFCCFF light pink
7448  clBufferStopBackground (TColor)0xFFFFCC pale cyan
7449  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
7450  clSignallerStopped (TColor)0x99CCFF caramel
7451  clNormalBackground (TColor)0xCCCCCC grey
7452 */
7453 
7454  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
7455  bool HideFlashingTrain = true;
7456  // hide it when Flash false so it blinks on and off
7457  // if don't hide it it stays displayed all the time
7458  Graphics::TBitmap *SmallTrainBitmap;
7459 
7460  // NB ensure retain same order as zoomed in order so colours correspond
7462  {
7463  TrainController->CrashWarning = true;
7464  SmallTrainBitmap = RailGraphics->smRed;
7465  }
7467  {
7469  SmallTrainBitmap = RailGraphics->smRed;
7470  }
7472  {
7473  TrainController->SPADWarning = true;
7474  SmallTrainBitmap = RailGraphics->smYellow;
7475  }
7477  {
7479  SmallTrainBitmap = RailGraphics->smOrange;
7480  }
7482  {
7484  SmallTrainBitmap = RailGraphics->smMagenta;
7485  }
7487  {
7489  SmallTrainBitmap = RailGraphics->smBrightGreen;
7490  }
7492  {
7494  SmallTrainBitmap = RailGraphics->smCyan;
7495  }
7497  {
7498  SmallTrainBitmap = RailGraphics->smPaleGreen;
7499  HideFlashingTrain = false;
7500  }
7502  {
7503  SmallTrainBitmap = RailGraphics->smCyan;
7504  HideFlashingTrain = false;
7505  }
7507  {
7508  SmallTrainBitmap = RailGraphics->smLightBlue;
7509  HideFlashingTrain = false;
7510  }
7512  {
7513  SmallTrainBitmap = RailGraphics->smCaramel;
7514  HideFlashingTrain = false;
7515  }
7516  else
7517  {
7518  SmallTrainBitmap = RailGraphics->smBlack; // moving
7519  HideFlashingTrain = false;
7520  }
7521 
7522  // now plot the new train
7523  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
7524  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
7525  {
7526  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
7527  }
7528  if((MidElement > -1) && (!HideFlashingTrain || Flash))
7529  {
7530  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
7531  }
7532  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
7533  {
7534  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
7535  }
7539  Utilities->CallLogPop(1459);
7540 }
7541 
7542 // ---------------------------------------------------------------------------
7543 
7545 {
7546  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
7547  if(!Display->ZoomOutFlag)
7548  {
7549  Utilities->CallLogPop(1304);
7550  return;
7551  }
7552  for(int y = 0; y < 3; y++)
7553  {
7554  if(OldZoomOutElement[y] > -1)
7555  {
7556  bool FoundFlag = false;
7557  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
7558  TTrackElement IATElement1, IATElement2;
7559  // default elements to begin with
7560  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
7561  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
7562  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
7563  if(FoundFlag)
7564  {
7565  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
7566  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
7567  if(IMPair.first != IMPair.second)
7568  {
7569  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
7570  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
7571  }
7572  }
7573  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
7574  }
7575  }
7576  Utilities->CallLogPop(1305);
7577 }
7578 
7579 // ---------------------------------------------------------------------------
7580 
7581 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
7582 {
7583  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
7584  LocationName = "";
7585  if(!StoppedAtLocation)
7586  {
7587  Utilities->CallLogPop(1398);
7588  return false;
7589  }
7590  if(LeadElement > -1)
7591  {
7593  }
7594  if((LocationName == "") && (MidElement > -1))
7595  {
7596  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
7597  }
7598  if((LocationName == "") && (LagElement > -1))
7599  {
7600  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
7601  }
7602  if(LocationName == "")
7603  {
7604  throw Exception("Error - Location name not set in TrainAtLocation");
7605  }
7606  Utilities->CallLogPop(1399);
7607  return true;
7608 }
7609 
7610 // ---------------------------------------------------------------------------
7611 
7612 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
7613 {
7614  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
7615  for(int x = 0; x < 4; x++)
7616  {
7617  PlotTrainGraphic(7, x, Disp);
7618  }
7619  Utilities->CallLogPop(647);
7620 }
7621 
7622 // ---------------------------------------------------------------------------
7623 
7624 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
7625 {
7626  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
7627  for(int x = 0; x < 4; x++)
7628  {
7629  if(PlotElement[x] > -1)
7630  {
7631  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
7632  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
7633  }
7634  }
7635  Utilities->CallLogPop(1708);
7636 }
7637 
7638 // ---------------------------------------------------------------------------
7639 
7640 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
7641 {
7642  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
7643  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
7644  AnsiString(LinkNumber) + "," + HeadCode);
7645 
7646 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
7647  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
7648  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
7649 */
7650 
7651  // note that MidElement always fully occupied
7652  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
7653  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
7654  {
7655  Utilities->CallLogPop(2005);
7656  return true;
7657  }
7658  if(Straddle == LeadMid)
7659  {
7660  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
7661  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
7662  {
7663  Utilities->CallLogPop(2006);
7664  return true;
7665  }
7666  }
7667  else if(Straddle == LeadMidLag)
7668  {
7669  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
7670  // only interested in LeadEntryPos as train not occupying ExitPos yet
7671  {
7672  Utilities->CallLogPop(2007);
7673  return true;
7674  }
7675  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
7676  // only interested in LagExitPos as train has left EntryPos
7677  {
7678  Utilities->CallLogPop(2008);
7679  return true;
7680  }
7681  }
7682  Utilities->CallLogPop(2009);
7683  return false;
7684 }
7685 
7686 // ---------------------------------------------------------------------------
7687 
7688 float TTrain::CalcTimeToAct(int Caller) // only called for running trains
7693 {
7694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
7695  int DistanceToRedSignal = 0;
7696  float TimeToAct = 0;
7697 
7698  if(TrainFailed)
7699  {
7700  Utilities->CallLogPop(2147);
7701  return 0; // time to act now
7702  }
7703 
7704  if(SignallerStopped)
7705  {
7706  Utilities->CallLogPop(2080);
7707  return -1;
7708  }
7709 
7710  if(!Stopped() || StoppedAtLocation)
7711  {
7712  // calc distance to next red signal but check for continuation exit
7713  if(LeadElement == -1)
7714  // if -1 it's on an end element so no action needed
7715  {
7716  Utilities->CallLogPop(2075);
7717  return -1;
7718  }
7719  else
7720  {
7721  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
7722  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
7723  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
7724 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
7725  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
7726  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
7727  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
7728  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
7729  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
7730  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
7731  before the train has stopped the current station is still recognised as a future stop.
7732  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
7733  becomes the signal, and the time to act indication becomes 'NOW'.
7734 */ {
7735  FirstPosToBeMeasured = LeadElement;
7736  FirstEntryPos = LeadEntryPos;
7737  }
7738  float CurrentStopTime; // set to 0 at start of function
7739  float LaterStopTime; // set to 0 at start of function
7740  float RecoverableTime; // set to 0 at start of function
7741  int AvTrackSpeed; // set to zero at start of function
7742  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
7743  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
7744  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
7745  if(DistanceToRedSignal == -1) // -1 for no action needed
7746  {
7747  Utilities->CallLogPop(2076);
7748  return -1;
7749  }
7750 /* Have MinsDelayed; pos or neg,
7751  CurrentStopTime; pos or zero
7752  LaterStopTime; pos or zero
7753  RecoverableTime; pos or zero
7754 
7755  & from these calculate TotalStopTime. noting that:
7756  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
7757  RecoverableTime always < LaterStopTime or both zero
7758  can't subtract more than RecoverableTime (MinsDelayed > 0)
7759  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
7760  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
7761  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
7762  if running early & stopped at location CurrentStopTime will automatically include the excess
7763 */
7764  float TimeToSubtract, TotalStopTime;
7765  if(MinsDelayed > RecoverableTime)
7766  TimeToSubtract = RecoverableTime;
7767  else
7768  TimeToSubtract = MinsDelayed; // may be negative;
7769 
7770  if(MinsDelayed < 0) // running early
7771  {
7772  if(CurrentStopTime > 0)
7773  TotalStopTime = CurrentStopTime + LaterStopTime;
7774  // stopped at loc, will depart on time
7775  else
7776  TotalStopTime = LaterStopTime - MinsDelayed;
7777  // not stopped, will depart on time at first later stop so add the delay
7778  }
7779  else // on time or running late
7780  {
7781  if(LaterStopTime == 0)
7782  TotalStopTime = CurrentStopTime;
7783  // no later stops, if stopped now will depart as soon as possible,
7784  // if not stopped no stop times to add
7785  else
7786  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
7787  }
7788 
7789  if(AvTrackSpeed < 30)
7790  AvTrackSpeed = 30;
7791  int Speed = AvTrackSpeed;
7792  if(AvTrackSpeed > int(MaxRunningSpeed))
7793  Speed = int(MaxRunningSpeed);
7794  if(TrainMode == Signaller)
7795  {
7796  Speed = SignallerMaxSpeed;
7797  TotalStopTime = 0;
7798  }
7799  TimeToAct = TotalStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
7800  // accel & decel taken into account in
7801  // CalcDistanceToRedSignalandStopTime
7802  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
7803  Utilities->CallLogPop(2079);
7804  return TimeToAct;
7805  }
7806  }
7807  else // stopped not at location
7808  {
7810  TimeToAct = 0.0;
7811  // but if stopped at a signal & autosigs route after it then ok
7812  if(StoppedAtSignal)
7813  {
7814  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
7815  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
7816  int NextExitPos;
7817  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
7818  {
7819  if((NextEntryPos == 0) || (NextEntryPos == 2))
7820  // leading entry point
7821  {
7822  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
7823  NextExitPos = 1;
7824  else
7825  NextExitPos = 3;
7826  }
7827  else
7828  NextExitPos = 0; // trailing entry point
7829  }
7830  else
7831  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
7832  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
7833  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
7834  int RouteNumber; // holder for referenced value, not used
7835  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
7836  {
7837  TimeToAct = -1;
7838  }
7839  }
7840  Utilities->CallLogPop(2074);
7841  return TimeToAct;
7842  }
7843 }
7844 
7845 // ---------------------------------------------------------------------------
7846 
7848 {
7849  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
7850  if(LeadElement > -1)
7851  {
7853  {
7854  Utilities->CallLogPop(2148);
7855  return true;
7856  }
7857  }
7858  if(MidElement > -1)
7859  {
7861  {
7862  Utilities->CallLogPop(2149);
7863  return true;
7864  }
7865  }
7866  if(LagElement > -1)
7867  {
7869  {
7870  Utilities->CallLogPop(2150);
7871  return true;
7872  }
7873  }
7874  Utilities->CallLogPop(2151);
7875  return false;
7876 }
7877 
7878 // ---------------------------------------------------------------------------
7879 // TTrainController
7880 // ---------------------------------------------------------------------------
7881 
7883 {
7884  OnTimeArrivals = 0;
7885  LateArrivals = 0;
7886  EarlyArrivals = 0;
7887  OnTimePasses = 0;
7888  LatePasses = 0;
7889  EarlyPasses = 0;
7890  OnTimeDeps = 0;
7891  LateDeps = 0;
7892  MissedStops = 0;
7893  OtherMissedEvents = 0;
7894  UnexpectedExits = 0;
7895  NumFailures = 0;
7896  IncorrectExits = 0;
7897  SPADEvents = 0;
7898  SPADRisks = 0;
7899  CrashedTrains = 0;
7900  Derailments = 0;
7901  TotArrDepPass = 0;
7902  TotLateArrMins = 0;
7903  TotEarlyArrMins = 0;
7904  TotLatePassMins = 0;
7905  TotEarlyPassMins = 0;
7906  TotLateDepMins = 0;
7907  ExcessLCDownMins = 0;
7908  TTClockTime = 0; // added for v0.6
7910  // added at v1.3.0 to ensure false at start
7911  OpTimeToActUpdateCounter = 0; // new v2.2.0
7912  OpActionPanelVisible = false; // new v2.2.0
7913  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
7914  SSHigh = false;
7915  MRSHigh = false;
7916  MRSLow = false;
7917  MassHigh = false;
7918  BFHigh = false;
7919  BFLow = false;
7920  PwrHigh = false;
7921  SigSHigh = false;
7922  SigSLow = false;
7923  randomize();
7924  // to seed rand() & random() with a random number (see UpdateTrain)
7925 }
7926 
7927 // ---------------------------------------------------------------------------
7928 
7930 {
7931  for(unsigned int x = 0; x < TrainVector.size(); x++)
7932  {
7933  TrainVectorAt(32, x).DeleteTrain(4);
7934  }
7935  TrainVector.clear();
7936 }
7937 
7938 // ---------------------------------------------------------------------------
7939 
7940 void TTrainController::LogEvent(AnsiString Str)
7941 {
7942  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
7943 
7944  // restrict to last 1000 entries
7945  Utilities->EventLog.push_back(FullStr);
7946  if(Utilities->EventLog.size() > 1000)
7947  Utilities->EventLog.pop_front();
7948 }
7949 
7950 // ---------------------------------------------------------------------------
7951 
7953 {
7954  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
7955  bool ClockState = Utilities->Clock2Stopped;
7956 
7957  Utilities->Clock2Stopped = true;
7958  // new section dealing with Snt & Snt-sh additions
7959  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
7960  // clock tick after stops flashing
7962  {
7963  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
7964  {
7965  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
7966  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
7967  TActionEventType EventType = NoEvent;
7968  if(AVEntry0.Command == "Snt")
7969  {
7970  // calc below only for Snt & Snt-sh entries rather than all entries to save time
7971  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
7972  int IncrementalMinutes = 0;
7973  int IncrementalDigits = 0;
7974  if(AVEntryLast.FormatType == Repeat)
7975  {
7976  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
7977  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
7978  }
7979  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
7980  {
7981  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
7982  }
7983  // see above note
7984 
7985  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
7986  {
7987  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
7988  if(TTOD.RunningEntry != NotStarted)
7989  continue;
7990  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
7991  break; // all the rest will also be greater
7992  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
7993  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
7994  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
7995  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
7996  {
7997  TTOD.TrainID = TrainVector.back().TrainID;
7998  TTOD.RunningEntry = Running;
7999  }
8000  else if(EventType == FailTrainEntry)
8001  {
8002  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8003  }
8004  }
8005  }
8006 
8007  if(AVEntry0.Command == "Snt-sh")
8008  // just start this once, shuttle repeats take care of restarts
8009  {
8010  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8011  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8012  int IncrementalMinutes = 0;
8013  int IncrementalDigits = 0;
8014  if(AVEntryLast.FormatType == Repeat)
8015  {
8016  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8017  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8018  }
8019  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8020  {
8021  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
8022  }
8023  // see above note
8024  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
8025  if(TTOD.RunningEntry == NotStarted)
8026  {
8027  if(AVEntry0.EventTime <= TTClockTime)
8028  {
8029  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8030  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
8031  TDEntry.SignallerSpeed, false, EventType))
8032  // false for SignallerControl
8033  {
8034  TTOD.TrainID = TrainVector.back().TrainID;
8035  TTOD.RunningEntry = Running;
8036  }
8037  else if(EventType == FailTrainEntry)
8038  {
8039  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8040  }
8041  }
8042  }
8043  }
8044  }
8045  }
8046 
8047  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
8048  // iteration, next cycle will catch up with any other pending updates
8049  if(!TrainVector.empty())
8050  {
8051  TrainAdded = false;
8052  AllRoutes->CallonVector.clear();
8053  // this will be rebuilt during the calls to UpdateTrain
8054  for(unsigned int x = 0; x < TrainVector.size(); x++)
8055  {
8056  TrainVectorAt(33, x).UpdateTrain(0);
8057  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
8058  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
8059  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
8060  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
8061  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
8062  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
8063  {
8064  break;
8065  }
8066  }
8067  // set warning flags
8068  CrashWarning = false;
8069  DerailWarning = false;
8070  SPADWarning = false;
8071  CallOnWarning = false;
8072  SignalStopWarning = false;
8073  BufferAttentionWarning = false;
8074  TrainFailedWarning = false;
8075  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
8076  {
8077  TTrain &Train = TrainVectorAt(34, x);
8078  if(Train.Crashed)
8079  // can't use background colours for crashed & derailed because same colour
8080  {
8081  CrashWarning = true;
8082  }
8083  else if(Train.Derailed)
8084  // can't use background colours for crashed & derailed because same colour
8085  {
8086  DerailWarning = true;
8087  }
8088  else if(Train.BackgroundColour == clSPADBackground)
8089  // use colour as that changes as soon as passes signal
8090  {
8091  SPADWarning = true;
8092  }
8093  else if(Train.BackgroundColour == clTrainFailedBackground)
8094  {
8095  TrainFailedWarning = true;
8096  }
8097  else if(Train.BackgroundColour == clCallOnBackground)
8098  // use colour as also stopped at signal
8099  {
8100  CallOnWarning = true;
8101  }
8102  else if(Train.BackgroundColour == clSignalStopBackground)
8103  // use colour to distinguish from call-on
8104  {
8105  SignalStopWarning = true;
8106  }
8107  else if(Train.BackgroundColour == clBufferAttentionNeeded)
8108  // use colour to distinguish from ordinary buffer stop
8109  {
8110  BufferAttentionWarning = true;
8111  }
8112  if(Train.HasTrainGone())
8113  {
8114  AnsiString Loc = "";
8115  bool ElementFound = false;
8116  TTrackElement TE;
8117  if(Train.LagElement > -1)
8118  {
8119  TE = Track->TrackElementAt(531, Train.LagElement);
8120  ElementFound = true;
8121  }
8122  else if(Train.MidElement > -1)
8123  {
8124  TE = Track->TrackElementAt(779, Train.MidElement);
8125  ElementFound = true;
8126  }
8127  else if(Train.LeadElement > -1)
8128  {
8129  TE = Track->TrackElementAt(780, Train.LeadElement);
8130  ElementFound = true;
8131  }
8132  if(ElementFound)
8133  {
8134  if(TE.ActiveTrackElementName != "")
8135  {
8136  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8137  }
8138  else
8139  {
8140  Loc = "track element " + TE.ElementID;
8141  }
8142  }
8143 
8144  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
8145  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
8146  // need above first because may also have ActionVectorEntryPtr == "Fer"
8147  {
8148  Train.UnplotTrain(9);
8149  // added at v1.3.0 to reset signals after train removed from an autosigsroute
8151  {
8154  }
8155  // end of addition
8156  AllRoutes->RebuildRailwayFlag = true;
8157  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
8158  // correctly after a crash
8159  }
8160  else if(AVEntryPtr->Command == "Fer")
8161  {
8162  bool CorrectExit = false;
8163  if(!AVEntryPtr->ExitList.empty())
8164  {
8165  for(TExitListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
8166  {
8167  if(*ELIT == Train.LagElement)
8168  CorrectExit = true;
8169  }
8170  }
8171  if(CorrectExit)
8172  {
8173  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
8174  }
8175  else
8176  {
8177  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
8178  }
8179  }
8180  else
8181  {
8182  if(!AVEntryPtr->SignallerControl)
8183  {
8184  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
8185  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
8186  // -2 is marker for send messages for all remaining actions except Fer if present
8187  }
8188  else
8189  {
8190  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
8191  }
8192  }
8193  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
8194  Train.DeleteTrain(1);
8195  TrainVector.erase(TrainVector.begin() + x);
8196  ReplotTrains(1, Display);
8197  // to reset ElementIDs for remaining trains when have removed a train
8198  }
8199  }
8200  }
8201  else
8202  {
8203  // reset all flags in case last train removed with flag set
8204  CrashWarning = false;
8205  DerailWarning = false;
8206  SPADWarning = false;
8207  CallOnWarning = false;
8208  SignalStopWarning = false;
8209  BufferAttentionWarning = false;
8210  TrainFailedWarning = false;
8211  }
8212 
8213  // update OpTimeToActMultimap
8215  {
8217  // clears entries then adds values for running trains then for continuation entries
8218  }
8219 
8220  Utilities->Clock2Stopped = ClockState;
8221  Utilities->CallLogPop(723);
8222 }
8223 
8224 // ---------------------------------------------------------------------------
8226 {
8227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
8228  if(!TrainVector.empty())
8229  {
8230  for(int x = TrainVector.size() - 1; x >= 0; x--)
8231  {
8232  TrainVectorAt(50, x).DeleteTrain(2);
8233  }
8234  TrainVector.clear();
8235  }
8236  if(!TrainDataVector.empty())
8237  {
8238  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8239  {
8240  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
8241  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8242  {
8243  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
8244  TOD.RunningEntry = NotStarted;
8245  TOD.TrainID = -1;
8246  TOD.EventReported = NoEvent;
8247  }
8248  }
8249  }
8250  Display->GetOutputLog1()->Caption = "";
8251  Display->GetOutputLog2()->Caption = "";
8252  Display->GetOutputLog3()->Caption = "";
8253  Display->GetOutputLog4()->Caption = "";
8254  Display->GetOutputLog5()->Caption = "";
8255  Display->GetOutputLog6()->Caption = "";
8256  Display->GetOutputLog7()->Caption = "";
8257  Display->GetOutputLog8()->Caption = "";
8258  Display->GetOutputLog9()->Caption = "";
8259  Display->GetOutputLog10()->Caption = "";
8260  Utilities->CallLogPop(1352);
8261 }
8262 
8263 // ---------------------------------------------------------------------------
8264 
8266 {
8267  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
8268  if(!TrainVector.empty())
8269  {
8270  for(unsigned int x = 0; x < TrainVector.size(); x++)
8271  {
8272  TrainVectorAt(51, x).PlotTrain(4, Disp);
8273  }
8274  }
8275  Utilities->CallLogPop(724);
8276 }
8277 
8278 // ---------------------------------------------------------------------------
8279 
8280 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
8281 {
8282  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
8283  if(!TrainVector.empty())
8284  {
8285  for(unsigned int x = 0; x < TrainVector.size(); x++)
8286  {
8287  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
8288  }
8289  }
8290  Utilities->CallLogPop(1707);
8291 }
8292 
8293 // ---------------------------------------------------------------------------
8294 
8296 {
8297  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
8298  if(!TrainVector.empty())
8299  {
8300  for(unsigned int x = 0; x < TrainVector.size(); x++)
8301  {
8302  TrainVectorAt(52, x).UnplotTrain(10);
8303  }
8304  }
8306  Utilities->CallLogPop(725);
8307 }
8308 
8309 // ---------------------------------------------------------------------------
8310 
8311 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
8312  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
8313  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
8314 {
8315  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
8316  "," + AnsiString(Mass) + "," + ModeStr);
8317  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
8318  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
8319 
8320  int RearExitPos = -1;
8321 
8322  for(int x = 0; x < 4; x++)
8323  {
8324  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
8325  {
8326  RearExitPos = x;
8327  }
8328  }
8329  if(RearExitPos == -1)
8330  {
8331  throw Exception("Error, RearExit == -1 in AddTrain");
8332  }
8333 
8334  bool ReportFlag = true;
8335 
8336  // used to stop repeated messages from CheckStartAllowable when split failed
8337  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
8338  ReportFlag = false;
8339 
8340  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
8341  { // messages sent to performance log in CheckStartAllowable if ReportFlag true
8342  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
8343  Utilities->CallLogPop(938);
8344  return false;
8345  }
8346 
8347  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
8348  TTrainMode TrainMode = NoMode;
8349 
8350  if(ModeStr == "Timetable")
8351  TrainMode = Timetable;
8352  // all else gives 'None', 'Signaller' set within program
8353 
8354  if(MaxRunningSpeed < 10)
8355  {
8356  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8357  }
8358  if(SignallerSpeed < 10)
8359  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
8360  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
8361  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
8362 
8363  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
8364  // initialise here rather than in TTrain constructor as create trains
8365  // with Null TrainDataEntryPtr when loading session trains
8366  if(SignallerControl)
8367  {
8368  NewTrain->TimetableFinished = true;
8369  NewTrain->SignallerStoppingFlag = false;
8370  NewTrain->TrainMode = Signaller;
8371  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
8372  {
8373  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
8374  }
8376  }
8377 
8378  // deal with starting conditions:-
8379  // unlocated Snt: just report entry & advance pointer
8380  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
8381  // Sns doesn't need a new train
8382  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
8383  // covers all above located starts
8384  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
8385  // wouldn't have accepted the timetable
8386  {
8387  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
8388  // StoppedAtBuffers is set in UpdateTrain()
8389  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
8390  // buffer end must be ahead of train or would have failed start position check
8391  {
8392  NewTrain->StoppedAtLocation = true;
8393  NewTrain->PlotStartPosition(0);
8395  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
8396  NewTrain->ActionVectorEntryPtr->Warning);
8397  if(!SignallerControl) // don't advance if SignalControlEntry
8398  {
8399  NewTrain->ActionVectorEntryPtr++;
8400  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
8401  }
8402  NewTrain->LastActionTime = TTClockTime;
8403  }
8404  // else a through station stop
8405  else
8406  {
8407  NewTrain->StoppedAtLocation = true;
8408  NewTrain->PlotStartPosition(10);
8410  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
8411  NewTrain->ActionVectorEntryPtr->Warning);
8412  if(!SignallerControl) // don't advance if SignalControlEntry
8413  {
8414  NewTrain->ActionVectorEntryPtr++;
8415  }
8416  NewTrain->LastActionTime = TTClockTime;
8417  }
8418  }
8419  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
8420  {
8421  NewTrain->PlotStartPosition(11);
8422  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
8423  AnsiString Loc = "";
8424  if(TE.ActiveTrackElementName != "")
8425  {
8426  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8427  }
8428  else
8429  {
8430  Loc = "track element " + TE.ElementID;
8431  }
8432  if(TE.TrackType == Continuation)
8433  {
8434  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
8435  }
8436  else
8437  {
8438  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
8439  }
8440  if(!SignallerControl) // don't advance if SignalControlEntry
8441  {
8442  NewTrain->ActionVectorEntryPtr++;
8443  }
8444  NewTrain->LastActionTime = TTClockTime;
8445  // no need to set LastActionTime for an unlocated entry
8446  }
8447 
8448  // cancel a wrong-direction route if either element of train starts on one
8449  if(NewTrain->LeadElement > -1)
8450  {
8451  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
8452  }
8453  if(NewTrain->MidElement > -1)
8454  {
8455  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
8456  }
8457 
8458  // set signals for a right-direction autosigs route for either element of train on one
8459  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
8460  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
8461  int RouteNumber = -1;
8462  bool SignalsSet = false;
8463 
8464  if(NewTrain->LeadElement > -1)
8465  {
8466  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8467  { // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
8468  int RouteStartPosition;
8469  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
8470  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
8471  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
8472  if(FirstPair.first == RouteNumber)
8473  {
8474  RouteStartPosition = FirstPair.second;
8475  }
8476  else if(SecondPair.first == RouteNumber)
8477  {
8478  RouteStartPosition = SecondPair.second;
8479  }
8480  else
8481  {
8482  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
8483  }
8484  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
8485  SignalsSet = true;
8486  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
8487  }
8488  else if(RouteNumber > -1) // non-autosigsroute
8489  {
8490  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
8491  int FirstTVPos = TempPDE.GetTrackVectorPosition();
8492  int FirstELinkPos = TempPDE.GetELinkPos();
8493  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
8494  {
8495  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8496  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
8497  }
8498  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
8499  {
8500  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8501  // remove the last element under LeadElement
8502  }
8503  AllRoutes->RebuildRailwayFlag = true;
8504  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
8505  // now deal with a rear linked autosigs route
8506  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
8507  {
8508  int LinkedRouteNumber = -1;
8509  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
8510  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
8511  {
8512  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
8513  // this is ok as here we are setting signals from the start of the route
8514  }
8515  }
8516  SignalsSet = true;
8517  }
8518  }
8519  if(NewTrain->MidElement > -1)
8520  // if entering at a continuation MidElement == -1
8521  { // this is included in case a train starts with LeadElement on no route and MidElement on a route
8522  if(!SignalsSet)
8523  {
8524  RouteNumber = -1;
8525  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8526  { // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
8527  int RouteStartPosition;
8528  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
8529  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
8530  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
8531  if(FirstPair.first == RouteNumber)
8532  {
8533  RouteStartPosition = FirstPair.second;
8534  }
8535  else if(SecondPair.first == RouteNumber)
8536  {
8537  RouteStartPosition = SecondPair.second;
8538  }
8539  else
8540  {
8541  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
8542  }
8543  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
8544  SignalsSet = true;
8545  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
8546  }
8547  else if(RouteNumber > -1) // non-autosigsroute
8548  {
8549  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
8550  int FirstTVPos = TempPDE.GetTrackVectorPosition();
8551  int FirstELinkPos = TempPDE.GetELinkPos();
8552  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
8553  {
8554  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8555  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
8556  }
8557  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
8558  {
8559  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8560  // remove the last element under LeadElement
8561  }
8562  AllRoutes->RebuildRailwayFlag = true;
8563  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
8564  // now deal with a rear linked autosigs route
8565  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
8566  {
8567  int LinkedRouteNumber = -1;
8568  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
8569  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
8570  {
8571  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
8572  // this is ok as now we are setting signals from the start of the route
8573  }
8574  }
8575  }
8576  }
8577  }
8578 
8579  TrainVector.push_back(*NewTrain);
8580  Utilities->CallLogPop(731);
8581  return true;
8582 }
8583 
8584 // ---------------------------------------------------------------------------
8585 
8586 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
8587 {
8588  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
8589  AnsiString(TrackVectorNumber));
8590  int VecPos = -1;
8591 
8592  for(unsigned int x = 0; x < TrainVector.size(); x++)
8593  {
8594  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
8595  {
8596  VecPos = x;
8597  }
8598  }
8599  if(VecPos == -1)
8600  {
8601  throw Exception("Error, VecPos not set in EntryPos");
8602  }
8603  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
8604  {
8605  Utilities->CallLogPop(734);
8606  return TrainVectorAt(3, VecPos).LeadEntryPos;
8607  }
8608  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
8609  {
8610  Utilities->CallLogPop(735);
8611  return TrainVectorAt(5, VecPos).MidEntryPos;
8612  }
8613  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
8614  {
8615  Utilities->CallLogPop(736);
8616  return TrainVectorAt(7, VecPos).LagEntryPos;
8617  }
8618  Utilities->CallLogPop(737);
8619  return -1;
8620 }
8621 
8622 // ---------------------------------------------------------------------------
8623 
8625 {
8626  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
8627  for(unsigned int x = 0; x < TrainVector.size(); x++)
8628  {
8629  if(TrainVectorAt(53, x).TrainID == TrainID)
8630  {
8631  Utilities->CallLogPop(738);
8632  return TrainVectorAt(54, x);
8633  }
8634  }
8635  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
8636 }
8637 
8638 // ---------------------------------------------------------------------------
8639 
8640 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
8641  // return true if find the train (added at v2.4.0 as can select a removed train
8642  // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
8643 {
8644  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
8645  for(unsigned int x = 0; x < TrainVector.size(); x++)
8646  {
8647  if(TrainVectorAt(69, x).TrainID == TrainID)
8648  {
8649  Utilities->CallLogPop(2152);
8650  return true;
8651  }
8652  }
8653  Utilities->CallLogPop(2153);
8654  return false;
8655 }
8656 
8657 // ---------------------------------------------------------------------------
8658 
8659 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
8660 {
8661  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
8662  Utilities->Format96HHMMSS(Time));
8663  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
8664 
8665  Utilities->CallLogPop(2061);
8666  return RepeatTime;
8667 }
8668 
8669 // ---------------------------------------------------------------------------
8670 
8671 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
8672  // Enter with Ptr pointing to first action to be listed (i.e. next action)
8673 {
8674  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
8675  AnsiString RetStr = "", PartStr = "";
8676  int Count = 0;
8677  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
8678 
8679  Ptr--; // because incremented at start of loop
8680  do
8681  {
8682  Ptr++;
8683  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
8684  {
8685  continue; // move past the starting entry
8686  }
8687  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
8688  break;
8689  if(Ptr->SignallerControl)
8690  {
8691  RetStr = "Train under signaller control";
8692  break;
8693  }
8694  if(Ptr->FormatType == TimeTimeLoc)
8695  {
8696  if(Ptr->ArrivalTime == Ptr->DepartureTime)
8697  {
8698  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
8699  }
8700  else
8701  {
8702  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
8703  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
8704  Count++; // because there are 2 entries
8705  }
8706  }
8707  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
8708  {
8709  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
8710  }
8711  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
8712  {
8713  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
8714  }
8715  else if(Ptr->FormatType == PassTime) // new
8716  {
8717  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
8718  }
8719  else if(Ptr->Command == "Fns")
8720  {
8721  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8722  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8723  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
8724  }
8725  else if(Ptr->Command == "F-nshs")
8726  {
8727  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8728  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
8729  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8730  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
8731  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
8732  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
8733  }
8734 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
8735  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
8736  {
8737  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8738  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
8739  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
8740  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8741  }
8742  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
8743  {
8744  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8745  Ptr->NonRepeatingShuttleLinkHeadCode, + " at " + Ptr->LocationName;
8746  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8747  }
8748  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
8749  {
8750  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8751  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
8752  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
8753  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8754  }
8755  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
8756  {
8757  PartStr = "Terminate at " + Ptr->LocationName;
8758  }
8759  else if(Ptr->Command == "Frh")
8760  {
8761  PartStr = "Terminate at " + Ptr->LocationName;
8762  }
8763  else if(Ptr->Command == "Fer")
8764  {
8765  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
8766  TrainController->GetExitLocationAndAt(3, Ptr->ExitList);
8767  }
8768  else if(Ptr->Command == "Fjo")
8769  {
8770  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
8771  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8772  }
8773  else if(Ptr->Command == "jbo")
8774  {
8775  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
8776  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8777  }
8778  else if(Ptr->Command == "fsp")
8779  {
8780  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
8781  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8782  }
8783  else if(Ptr->Command == "rsp")
8784  {
8785  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
8786  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8787  }
8788  else if(Ptr->Command == "cdt")
8789  {
8790  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
8791  }
8792  if(RetStr != "")
8793  RetStr = RetStr + '\n' + PartStr;
8794  else
8795  RetStr = PartStr;
8796  Count++;
8797  }
8798  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
8799  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
8800  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
8801  // forward as anyone should wish to see without looking at the full timetable
8802  Utilities->CallLogPop(2072);
8803  return RetStr;
8804 }
8805 
8806 // ---------------------------------------------------------------------------
8807 
8808 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
8809 {
8810  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
8811  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
8812  AnsiString DepTime = "", EventTime = "";
8813  bool CDTFlag = false; //reports if train changes direction before departs
8814  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
8815  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
8816  {
8817  if(AVI->Command == "cdt")
8818  {
8819  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
8820  continue;
8821  }
8822  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
8823  {
8824  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
8825  RetStr += "\nNew service splits at " + EventTime;
8826  Utilities->CallLogPop(2237);
8827  return RetStr;
8828  }
8829  if(AVI->Command == "jbo")
8830  {
8831  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
8832  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
8833  Utilities->CallLogPop(2238);
8834  return RetStr;
8835  }
8836  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
8837  {
8838  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
8839  if(CDTFlag)
8840  {
8841  RetStr += "\nNew service changes direction then departs at " + DepTime;
8842  }
8843  else
8844  {
8845  RetStr += "\nNew service departs at " + DepTime;
8846  }
8847  Utilities->CallLogPop(2239);
8848  return RetStr;
8849  }
8850  }
8851  Utilities->CallLogPop(2223);
8852  return RetStr;
8853 }
8854 
8855 // ---------------------------------------------------------------------------
8856 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
8857 /*
8858  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
8859  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
8860  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
8861 
8862  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
8863  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
8864  user wishes
8865 
8866  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
8867  descriptive text or anything user wishes
8868  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
8869  be ignored) is taken as the timetable start time.
8870  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
8871  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
8872  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
8873  within the timetable if required.
8874  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
8875  services)
8876  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
8877  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
8878 
8879  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
8880  text timetable file easier
8881 
8882  form:-
8883  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
8884  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
8885  then multiple entries, separated by commas, of the form:-
8886 
8887  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
8888  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
8889  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
8890 
8891  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
8892  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
8893  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
8894 
8895  HH:MM;Command (cdt) }TimeCmd }
8896  HH:MM;Location (arr & dep) }TimeLoc }
8897  HH:MM;HH:MM;Location }TimeTimeLoc }
8898  HH:MM;pas;Location }PassTime }
8899  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
8900  HH:MM;Fer;set of allowable IDs }ExitRailway }
8901  Command (Frh only) }FinRemHere }
8902 
8903  R;mm;dd;nn. Repeat Repeat entry
8904 
8905  Formats:
8906 
8907  Command only: Frh
8908  Time;Command: cdt
8909  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
8910  Time;Command;2 Element IDs: Snt
8911  Time;Comand;n Element IDs: Fer
8912  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
8913  Time;Command;2 Element IDs;Headcode Snt-sh
8914  Time;Command;Location pas
8915  Time;Location Arr Dep
8916  Time;Time;Location Arr & dep together
8917 
8918  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
8919 
8920  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
8921  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
8922  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
8923 
8924  4 2x Linked entries, all shuttles:
8925 
8926  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
8927  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
8928  -> Remain Here (at finish location after all repeats)
8929  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
8930  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
8931 
8932  Allowable successors:-
8933 
8934  Successor state Type
8935 
8936  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
8937  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
8938  Sfs AtLoc )
8939  Sns AtLoc ) Start
8940  Sns-fsh AtLoc )
8941  Snt-sh AtLoc )
8942  Sns-sh AtLoc )
8943 
8944  pas Moving )
8945  jbo AtLoc )
8946  fsp AtLoc )
8947  rsp AtLoc ) Intermediate
8948  cdt AtLoc )
8949  TimeLoc arr Moving (bef) )
8950  TimeLoc dep AtLoc (bef) )
8951  TimeTimeLoc Moving )
8952 
8953  Fns Repeat/Nothing)
8954  Fjo Repeat/Nothing)
8955  Frh Repeat/Nothing)
8956  Fer Repeat/Nothing) Finish
8957  Frh-sh Repeat )
8958  Fns-sh Repeat )
8959  F-nshs Nothing )
8960 
8961  Descriptions:
8962  Snt New train
8963  Sfs New service from split
8964  Sns New service from another service
8965  Sns-fsh New non-repeating service from a shuttle service
8966  Snt-sh New shuttle train at a timetabled stop
8967  Sns-sh New shuttle service from a feeder service
8968 
8969  pas Pass
8970  jbo Be joined by another train
8971  fsp Front split
8972  rsp Rear split
8973  cdt Change direction of train
8974  TimeLoc arr Arrival
8975  TimeLoc dep Departure
8976  TimeTimeLoc Arrival and departure
8977 
8978  Fns Finish & form a new service
8979  Fjo Finish & join another train
8980  Frh Finish & remain here
8981  Fer Finish & exit railway
8982  Frh-sh Finish & repeat shuttle, finally remain here
8983  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
8984  F-nshs Finish & form a shuttle feeder service
8985 */
8986 
8987 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
8988 {
8989  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
8990  // a line that is too long; timetable containing too few lines; and timetable failed to open.
8991  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
8992  // new for v0.2b
8993  // compile ActiveTrackElementNameMap
8994  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
8995 
8997  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
8998  {
8999  // if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->TrackVector.at(x).TrackType != Continuation))
9000  if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->ContinuationNameMap.find(Track->TrackVector.at(x).ActiveTrackElementName))
9001  == Track->ContinuationNameMap.end())
9002  { // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
9003  ActiveTrackElementNameMapEntry.first = Track->TrackVector.at(x).ActiveTrackElementName;
9004  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
9005  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
9006  }
9007  }
9009  // end of new section
9010  std::ifstream TTBLFile(FileName, std::ios_base::binary);
9011 
9012  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
9013  if(TTBLFile.is_open())
9014  {
9015  char *TrainTimetableString = new char[10000];
9016  // enough for over 200 stations, should be adequate!
9017  bool EndOfFile = false;
9018  int Count = 0;
9019  // counts 'relevant' lines, i.e ignores any before the start time on its own line
9020  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9021  // delimiter is '\0' as it's an AnsiString
9022  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9023  // file empty - stores a null in 1st position if doesn't load any characters
9024  { // may still have eof even if read a line (no CRLF at end), and
9025  // if so need to process it
9026  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
9027  TTBLFile.close();
9028  delete TrainTimetableString;
9029  Utilities->CallLogPop(1611);
9030  return false;
9031  }
9032  AnsiString OneLine(TrainTimetableString);
9033  bool FinalCallFalse = false;
9034  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9035  // get rid of lines before the start time
9036  {
9037  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
9038  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9039  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9040  // stores a null in 1st position if doesn't load any characters
9041  { // may still have eof even if read a line (no CRLF at end), and
9042  // if so need to process it
9043  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
9044  TTBLFile.close();
9045  delete TrainTimetableString;
9046  Utilities->CallLogPop(772);
9047  return false;
9048  }
9049  OneLine = AnsiString(TrainTimetableString);
9050  }
9051  // here when have accepted the start time
9052  Count++; // increment past the start time
9053  while(!EndOfFile)
9054  {
9055  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9056  // get next line after start time
9057  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9058  // stores a null in 1st position if doesn't load any characters
9059  { // may still have eof even if read a line (no CRLF at end), and
9060  // if so need to process it
9061  EndOfFile = true;
9062  OneLine = "";
9063  }
9064  else
9065  OneLine = AnsiString(TrainTimetableString);
9066  if(OneLine.Length() > 9999)
9067  {
9068  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
9069  TTBLFile.close();
9070  delete TrainTimetableString;
9071  Utilities->CallLogPop(789);
9072  return false;
9073  }
9074  bool FinalCallFalse = false;
9075  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9076  // false for FinalCall - just checking at this stage
9077  {
9078  TTBLFile.close();
9079  delete TrainTimetableString;
9080  Utilities->CallLogPop(770);
9081  return false;
9082  }
9083  if(EndOfFile && (Count < 2))
9084  // Timetable must contain at least two relevant lines, one for start time and at least one train
9085  {
9086  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
9087  TTBLFile.close();
9088  delete TrainTimetableString;
9089  Utilities->CallLogPop(771);
9090  return false;
9091  }
9092  Count++;
9093  }
9094  delete TrainTimetableString;
9095  TTBLFile.close();
9096  } // if(TTBLFile.is_open())
9097  else
9098  {
9099  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's not open in another application");
9100  Utilities->CallLogPop(2154);
9101  return false;
9102  }
9103  Utilities->CallLogPop(753);
9104  return true;
9105 }
9106 
9107 // ---------------------------------------------------------------------------
9108 
9109 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
9110  bool CheckLocationsExistInRailway) // return true for success
9111 
9112 /* Format:
9113  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9114  descriptive text or anything user wishes
9115  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9116  be ignored) is taken as the timetable start time.
9117  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9118  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9119  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9120  within the timetable if required.
9121  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9122  services)
9123  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9124  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9125 
9126  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9127  text timetable file easier
9128 
9129  form:-
9130  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9131  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9132  then multiple entries, separated by commas, of the form:-
9133 
9134  Format FormatType
9135  [W]HH:MM;Command (cdt) }TimeCmd }
9136  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
9137  [W]HH:MM;pas;Location }PassTime }
9138  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9139  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
9140  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
9141  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
9142  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
9143  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
9144  [W]HH:MM;Fns-sh;Details }FSHNewService }
9145  [W]HH:MM;Location (arr & dep) }TimeLoc }
9146  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
9147  Command (Frh only) }FinRemHere }
9148 
9149  R;mm;dd;nn. Repeat Repeat entry
9150 
9151  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
9152  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
9153  at location; or (c) departure time if train already at location (including train started at location either as a new
9154  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
9155  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
9156  minutes, incremental train headcode last 2 digits, and number of repeats.
9157 
9158  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
9159  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
9160  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
9161  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
9162  (it's for a shuttle train to return to depot at end of services)
9163 
9164  Command/Location & details are as follows:-
9165 
9166  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
9167  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
9168  2E44 in its Sfs entry. All these are checked.
9169  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
9170 
9171  Start commands:-
9172  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
9173  with loc as a start entry can't have a location as details)
9174  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
9175  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
9176  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
9177  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
9178  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
9179  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
9180  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
9181  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
9182  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
9183  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
9184  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
9185 
9186  Intermediate commands:-
9187  Time - Location (TimeLoc), can be arrival or departure depending on context
9188  Time Time location (TimeTimeLoc), arrival and departure
9189  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
9190  pas (PassTime), Time;pas;Location
9191  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
9192  joining train's finish details must correspond or the file check will fail
9193  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
9194  new train - that train's starting information must correspond)
9195  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
9196  new train - that train's starting information must correspond)
9197  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
9198 
9199  Finish commands:-
9200  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
9201  creation)
9202  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
9203  shuttle headcode (no train creation)
9204  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
9205  may have to wait for it), details = new headcode (delete train)
9206  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
9207  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
9208  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
9209  here
9210  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
9211  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
9212 
9213  Repeat:-
9214  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
9215  headcodes - it is up to user to avoid duplicates if he/she wishes to.
9216 
9217  Checks carried out with error messages in this function:-
9218  At least one comma in a service line (it's based on a .csv file)
9219  No entries following train information;
9220  At least one comma in remainder after train information (i.e at least a start and a finish entry);
9221  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
9222  First entry not a start entry;
9223  Train information incomplete before a start entry;
9224  Entry follows a finish entry but doesn't begin with 'R';
9225  SplitEntry returns false in a finish entry - message repeats the entry for information;
9226  Last action entry isn't a finish entry.
9227 
9228  Function returns false with no message if:-
9229  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
9230  time is found at all then an error message is given in the calling function);
9231  SplitTrainInfo returns false (message given in called function);
9232  SplitRepeat returns false (message given in called function).
9233 */ {
9234  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
9235  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
9236  TTrainDataEntry TempTrainDataEntry;
9237 
9238  EndOfFile = false;
9239  StripSpaces(0, OneLine);
9240  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
9241  // semicolons within the line
9242  ServiceReference = "";
9243  if(OneLine != "")
9244  {
9245  if(OneLine[1] != '*')
9246  {
9247  int SCPos = OneLine.Pos(';');
9248  if(SCPos == 0)
9249  ServiceReference = OneLine.SubString(1, 8);
9250  else
9251  ServiceReference = OneLine.SubString(1, (SCPos - 1));
9252  }
9253  }
9254  bool AllCommas = true;
9255 
9256  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
9257  {
9258  if(OneLine[x] != ',')
9259  AllCommas = false;
9260  }
9261  if(AllCommas || (OneLine == ""))
9262  {
9263  if(Count > 0)
9264  {
9265  EndOfFile = true;
9266  // returns true for a blank line - treated as end of file
9267  Utilities->CallLogPop(1018);
9268  return true;
9269  }
9270  else // count == 0 so not yet found a start time, no message to be given
9271  {
9272  Utilities->CallLogPop(754);
9273  return false;
9274  }
9275  }
9276  AnsiString First = "", Second = "", Third = "", Fourth = "";
9277  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
9278  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
9279  TDateTime StartTime(0);
9280  TExitList ExitList;
9281  bool Warning = false;
9282 
9283  if(Count == 0) // no start time found yet
9284  {
9285 /* dropped at v0.6b
9286  AnyHeadCodeValid = false;
9287  if(OneLine.SubString(6,5) == ";0000")
9288  {
9289  AnyHeadCodeValid = true;
9290  }
9291 */
9292  if(!CheckTimeValidity(0, OneLine, StartTime))
9293  {
9294  // no message is given for an invalid time as it's assumed to be an irrelevant line
9295  // if no start time is found at all then an error message is given in the calling function
9296  // AnyHeadCodeValid = false;
9297  Utilities->CallLogPop(755);
9298  return false;
9299  }
9300  if(FinalCall) // here if start time valid
9301  {
9302  TTClockTime = StartTime;
9303  TimetableStartTime = StartTime;
9304  }
9305  }
9306  else
9307  {
9308  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
9309  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
9310  double MaxBrakeRate = 0;
9311  double PowerAtRail = 0;
9312  int SignallerSpeed = 0;
9313  if(OneLine[1] == '*')
9314  {
9315  Utilities->CallLogPop(1581);
9316  return true;
9317  // ignore any line beginning with '*' but return true as there is no error
9318  }
9319  int Pos = OneLine.Pos(',');
9320  if(Pos == 0)
9321  {
9322  int SubStringLength = 20;
9323  if(OneLine.Length() < 20)
9324  SubStringLength = OneLine.Length();
9325  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
9326  Utilities->CallLogPop(766);
9327  return false;
9328  }
9329  TrainInfoStr = OneLine.SubString(1, Pos - 1);
9330  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
9331  GiveMessages)) // error messages given in SplitTrainInfo
9332  {
9333  Utilities->CallLogPop(773);
9334  return false;
9335  }
9336  if(FinalCall)
9337  {
9338  // store Train info - conversions done in SplitTrainInfo
9339  // only headcode mandatory for continuing services
9340  TempTrainDataEntry.HeadCode = HeadCode;
9341  TempTrainDataEntry.ServiceReference = HeadCode;
9342  TempTrainDataEntry.Description = Description;
9343  TempTrainDataEntry.StartSpeed = StartSpeed;
9344  TempTrainDataEntry.Mass = Mass;
9345  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
9346  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
9347  TempTrainDataEntry.PowerAtRail = PowerAtRail;
9348  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
9349  TTrainOperatingData TempTrainOperatingData;
9350  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
9351  }
9352  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
9353  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
9354  // so strip them off
9355  while(NewRemainder[NewRemainder.Length()] == ',')
9356  {
9357  if(NewRemainder.Length() > 1)
9358  {
9359  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
9360  }
9361  else
9362  {
9363  NewRemainder = "";
9364  break;
9365  }
9366  }
9367  // check if zero length & fail if so
9368  if(NewRemainder == "")
9369  {
9370  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
9371  Utilities->CallLogPop(769);
9372  return false;
9373  }
9374  // now have one more entry than there are commas
9375  int CommaCount = 0;
9376  for(int x = 1; x < NewRemainder.Length() + 1; x++)
9377  {
9378  if(NewRemainder[x] == ',')
9379  CommaCount++;
9380  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
9381  if(CommaCount == 0)
9382  {
9383  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
9384  {
9385  int SubStringLength = 20;
9386  if(OneLine.Length() < 20)
9387  SubStringLength = OneLine.Length();
9388  TimetableMessage(GiveMessages,
9389  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
9390  OneLine.SubString(1, SubStringLength) + "'....");
9391  Utilities->CallLogPop(783);
9392  return false;
9393  }
9394  }
9395  AnsiString OneEntry = "";
9396  TTimetableFormatType FormatType;
9397  TTimetableSequenceType SequenceType;
9398  TTimetableLocationType LocationType;
9399  TTimetableShuttleLinkType ShuttleLinkType;
9400  bool FinishFlag = false;
9401  for(int x = 0; x < CommaCount + 1; x++)
9402  {
9403  if((CommaCount == 0) || (x < CommaCount))
9404  // i.e. train entered under signaller control with no repeats, or entry is not the last,
9405  // in which case there's a comma & finish element or repeat still to come this entry could
9406  // be a finish but can't be a repeat
9407  {
9408  if(CommaCount == 0)
9409  {
9410  OneEntry = NewRemainder;
9411  NewRemainder = "";
9412  }
9413  else
9414  {
9415  Pos = NewRemainder.Pos(',');
9416  OneEntry = NewRemainder.SubString(1, Pos - 1);
9417  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
9418  }
9419  First = "";
9420  Second = "";
9421  Third = "";
9422  Fourth = "";
9423  RearStartOrRepeatMins = 0;
9424  FrontStartOrRepeatDigits = 0;
9425  NumberOfRepeats = 0;
9426  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
9427  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
9428  {
9429  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
9430  Utilities->CallLogPop(756);
9431  return false;
9432  }
9433  // check if warning for Frh or Fjo & reject
9434  if(Warning && (Second == "Frh"))
9435  {
9436  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
9437  Utilities->CallLogPop(1793);
9438  return false;
9439  }
9440  if(Warning && (Second == "Fjo"))
9441  {
9442  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
9443  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
9444  Utilities->CallLogPop(1794);
9445  return false;
9446  }
9447  if(x == 0) // should be start event
9448  {
9449  if(SequenceType != Start)
9450  {
9451  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
9452  Utilities->CallLogPop(784);
9453  return false;
9454  }
9455  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
9456  {
9457  if(NewRemainder[1] != 'R')
9458  {
9459  TimetableMessage(GiveMessages,
9460  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
9461  OneEntry + "'");
9462  Utilities->CallLogPop(787);
9463  return false;
9464  }
9465  }
9466  if((Second == "Snt") || (Second == "Snt-sh"))
9467  // need full train information including non-default values for at least HeadCode, Description,
9468  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
9469  {
9470  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
9471  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
9472  {
9473  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
9474  OneEntry + "'");
9475  Utilities->CallLogPop(1783);
9476  return false;
9477  }
9478  }
9479  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
9480  // service continuation - need at least non-default value for HeadCode
9481  {
9482  if(HeadCode == "")
9483  {
9484  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
9485  OneEntry + "'");
9486  Utilities->CallLogPop(788);
9487  return false;
9488  }
9489  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
9490  {
9491  TimetableMessage(GiveMessages,
9492  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
9493  OneEntry + "'");
9494  Utilities->CallLogPop(843);
9495  return false;
9496  }
9497  }
9498  }
9499  if(SequenceType == Finish)
9500  {
9501  FinishFlag = true;
9502  // marker for only permitted additional entry being a repeat, only needed if the
9503  // finish entry is not the last entry
9504  }
9505  if(FinalCall)
9506  {
9507  // interpret & add to ActionVector
9508  TDateTime TempTime;
9509  TActionVectorEntry ActionVectorEntry;
9510  ActionVectorEntry.FormatType = FormatType;
9511  ActionVectorEntry.LocationType = LocationType;
9512  ActionVectorEntry.SequenceType = SequenceType;
9513  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
9514  ActionVectorEntry.Warning = Warning;
9515  if(FormatType == TimeLoc)
9516  {
9517  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
9518  {;
9519  } // these will all be true as final call
9520  ActionVectorEntry.LocationName = Second;
9521  }
9522  else if(FormatType == PassTime) // new
9523  {
9524  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
9525  {;
9526  }
9527  ActionVectorEntry.Command = Second;
9528  ActionVectorEntry.LocationName = Third;
9529  }
9530  else if(FormatType == TimeTimeLoc)
9531  {
9532  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
9533  {;
9534  }
9535  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
9536  {;
9537  }
9538  ActionVectorEntry.LocationName = Third;
9539  }
9540  else if(FormatType == TimeCmd)
9541  {
9542  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
9543  {;
9544  }
9545  ActionVectorEntry.Command = Second;
9546  }
9547  else if(FormatType == ExitRailway)
9548  {
9549  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
9550  {;
9551  }
9552  ActionVectorEntry.Command = Second;
9553  ActionVectorEntry.ExitList = ExitList;
9554  }
9555  else if(FormatType == StartNew)
9556  {
9557  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
9558  {;
9559  }
9560  ActionVectorEntry.Command = Second;
9561  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9562  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9563  if(Fourth == 'S')
9564  ActionVectorEntry.SignallerControl = true;
9565  }
9566  else if(FormatType == SNTShuttle)
9567  {
9568  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
9569  {;
9570  }
9571  ActionVectorEntry.Command = Second;
9572  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9573  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9574  ActionVectorEntry.OtherHeadCode = Fourth;
9575  }
9576  else if(FormatType == SNSShuttle)
9577  {
9578  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
9579  {;
9580  }
9581  ActionVectorEntry.Command = Second;
9582  ActionVectorEntry.OtherHeadCode = Third;
9583  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9584  }
9585  else if(FormatType == TimeCmdHeadCode)
9586  {
9587  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
9588  {;
9589  }
9590  ActionVectorEntry.Command = Second;
9591  ActionVectorEntry.OtherHeadCode = Third;
9592  }
9593  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
9594  {
9595  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
9596  {;
9597  }
9598  ActionVectorEntry.Command = Second;
9599  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
9600  }
9601  else if(FormatType == FSHNewService)
9602  {
9603  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
9604  {;
9605  }
9606  ActionVectorEntry.Command = Second;
9607  ActionVectorEntry.OtherHeadCode = Third;
9608  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9609  }
9610  else if(FormatType == FinRemHere)
9611  {
9612  ActionVectorEntry.Command = Second;
9613  }
9614  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9615  }
9616  }
9617  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
9618  {
9619  OneEntry = NewRemainder;
9620  First = "";
9621  Second = "";
9622  Third = "";
9623  Fourth = "";
9624  RearStartOrRepeatMins = 0;
9625  FrontStartOrRepeatDigits = 0;
9626  NumberOfRepeats = 0;
9627  if((FinishFlag) && (OneEntry[1] != 'R'))
9628  // already had a finish entry
9629  {
9630  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
9631  Utilities->CallLogPop(79);
9632  return false;
9633  }
9634  if(OneEntry[1] != 'R') // must be finish
9635  {
9636  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
9637  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
9638  {
9639  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
9640  Utilities->CallLogPop(757);
9641  return false;
9642  }
9643  if(SequenceType != Finish)
9644  {
9645  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
9646  Utilities->CallLogPop(785);
9647  return false;
9648  }
9649  if(FinalCall)
9650  {
9651  // interpret & add to ActionVector
9652  TDateTime TempTime;
9653  TActionVectorEntry ActionVectorEntry;
9654  ActionVectorEntry.FormatType = FormatType;
9655  ActionVectorEntry.LocationType = LocationType;
9656  ActionVectorEntry.SequenceType = SequenceType;
9657  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
9658  ActionVectorEntry.Warning = Warning;
9659  if(FormatType == TimeCmd)
9660  {
9661  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
9662  {;
9663  }
9664  ActionVectorEntry.Command = Second;
9665  }
9666  else if(FormatType == TimeCmdHeadCode)
9667  {
9668  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
9669  {;
9670  }
9671  ActionVectorEntry.Command = Second;
9672  ActionVectorEntry.OtherHeadCode = Third;
9673  }
9674  else if(FormatType == FNSNonRepeatToShuttle)
9675  {
9676  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
9677  {;
9678  }
9679  ActionVectorEntry.Command = Second;
9680  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
9681  }
9682  else if(FormatType == FSHNewService)
9683  {
9684  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
9685  {;
9686  }
9687  ActionVectorEntry.Command = Second;
9688  ActionVectorEntry.OtherHeadCode = Third;
9689  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9690  }
9691  else if(FormatType == ExitRailway)
9692  {
9693  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
9694  {;
9695  }
9696  ActionVectorEntry.Command = Second;
9697  ActionVectorEntry.ExitList = ExitList;
9698  }
9699  else if(FormatType == FinRemHere)
9700  {
9701  ActionVectorEntry.Command = Second;
9702  }
9703  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9704  }
9705  }
9706  else // repeat
9707  {
9708  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
9709  {
9710  Utilities->CallLogPop(786);
9711  // error messages given in SplitRepeat
9712  return false;
9713  }
9714  if(FinalCall)
9715  {
9716  TActionVectorEntry ActionVectorEntry;
9717  ActionVectorEntry.FormatType = Repeat;
9718  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
9719  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
9720  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
9721  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9722  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9723  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
9724  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9725  }
9726  }
9727  }
9728  }
9729  if(FinalCall)
9730  {
9731  TrainDataVector.push_back(TempTrainDataEntry);
9732  }
9733  }
9734  Utilities->CallLogPop(80);
9735  return true;
9736 }
9737 
9738 // ---------------------------------------------------------------------------
9739 
9740 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
9741 {
9742  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
9743  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
9744  {
9745  Utilities->CallLogPop(1890);
9746  return false;
9747  }
9748  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
9749  {
9750  Utilities->CallLogPop(1891);
9751  return false;
9752  }
9753  Utilities->CallLogPop(1892);
9754  return true;
9755 }
9756 
9757 // ---------------------------------------------------------------------------
9758 
9759 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
9760  // 1st 5 chars must be HH:MM, anything else will be ignored
9761 {
9762  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
9763  if(TimeStr.Length() < 5)
9764  {
9765  Utilities->CallLogPop(926);
9766  return false;
9767  }
9768  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
9769  {
9770  Utilities->CallLogPop(927);
9771  return false;
9772  }
9773  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
9774  {
9775  Utilities->CallLogPop(928);
9776  return false;
9777  }
9778  if(TimeStr[3] != ':')
9779  {
9780  Utilities->CallLogPop(929);
9781  return false;
9782  }
9783  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
9784  {
9785  Utilities->CallLogPop(930);
9786  return false;
9787  }
9788  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
9789  {
9790  Utilities->CallLogPop(931);
9791  return false;
9792  }
9793  while(TimeStr.Length() > 5)
9794  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
9795  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
9796  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
9797 
9798  if((WholeHours + FracHour) >= 95.98334)
9799  {
9800  Utilities->CallLogPop(1817);
9801  return false; // > 95h 59m
9802  }
9803  Time = TDateTime((WholeHours + FracHour) / 24);
9804  Utilities->CallLogPop(932);
9805  return true;
9806 }
9807 
9808 // ---------------------------------------------------------------------------
9809 
9810 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
9811  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
9812  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
9813 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
9814  Return false for failure.
9815  See description above under ProcessOneTimetableLinefor details of train action entries
9816  NB all types set except LocationType for Sns as may be located or not
9817 */ {
9818  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
9819  Warning = false;
9820  TDateTime TempTime;
9821 
9822  if(OneEntry.Length() > 0)
9823  {
9824  if(OneEntry[1] == 'W') // warning
9825  {
9826  Warning = true;
9827  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
9828  // strip it off
9829  }
9830  }
9831  if(OneEntry == "Frh")
9832  {
9833  FormatType = FinRemHere;
9834  SequenceType = Finish;
9835  LocationType = AtLocation;
9836  ShuttleLinkType = NotAShuttleLink;
9837  Second = "Frh";
9838  Utilities->CallLogPop(1016);
9839  return true;
9840  }
9841  if(OneEntry.Length() < 7)
9842  {
9843  Utilities->CallLogPop(907);
9844  return false; // 'HH:MM;' + at least a one-letter location name
9845  }
9846  int Pos = OneEntry.Pos(';'); // first segment delimiter
9847 
9848  if(Pos != 6)
9849  {
9850  Utilities->CallLogPop(908);
9851  return false;
9852  // no delimiter or delimiter not in position 6, has to be a time so fail
9853  }
9854  First = OneEntry.SubString(1, 5); // has to be a time
9855  if(!CheckTimeValidity(16, First, TempTime))
9856  {
9857  Utilities->CallLogPop(909);
9858  return false;
9859  }
9860  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
9861 
9862  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
9863  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
9864  {
9865  if(Remainder.Length() < 7)
9866  {
9867  Utilities->CallLogPop(910);
9868  return false; // 'HH:MM;' + at least a one-letter location name
9869  }
9870  Pos = Remainder.Pos(';'); // second segment delimiter
9871  if(Pos == 0)
9872  {
9873  Utilities->CallLogPop(911);
9874  return false;
9875  // no delimiter, has to be one between departure time & location
9876  }
9877  Second = Remainder.SubString(1, 5); // has to be a time
9878  if(!CheckTimeValidity(15, Second, TempTime))
9879  {
9880  Utilities->CallLogPop(912);
9881  return false;
9882  }
9883  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9884  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
9885  {
9886  Utilities->CallLogPop(913);
9887  return false;
9888  }
9889  FormatType = TimeTimeLoc;
9890  SequenceType = Intermediate;
9891  LocationType = AtLocation;
9892  ShuttleLinkType = NotAShuttleLink;
9893  Utilities->CallLogPop(914);
9894  return true;
9895  }
9896  Pos = Remainder.Pos(';'); // second segment delimiter
9897  if(Pos == 0) // no third segment so second must be a location, or cdt
9898  {
9899  Second = Remainder;
9900  if(Second == "cdt")
9901  {
9902  FormatType = TimeCmd;
9903  ShuttleLinkType = NotAShuttleLink;
9904  LocationType = AtLocation;
9905  SequenceType = Intermediate;
9906  Utilities->CallLogPop(915);
9907  return true;
9908  }
9909  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
9910  {
9911  Utilities->CallLogPop(916);
9912  return false;
9913  }
9914  else
9915  {
9916  FormatType = TimeLoc;
9917  LocationType = AtLocation;
9918  SequenceType = Intermediate;
9919  ShuttleLinkType = NotAShuttleLink;
9920  Utilities->CallLogPop(917);
9921  return true;
9922  }
9923  }
9924  // here if second segment is a command, with a third & maybe fourth segments as details
9925  if((Pos != 4) && (Pos != 7) && (Pos != 8))
9926  {
9927  Utilities->CallLogPop(918);
9928  return false;
9929  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
9930  }
9931  Second = Remainder.SubString(1, Pos - 1); // command
9932 
9933  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9934  // details
9935  Pos = Remainder.Pos(';'); // third segment delimiter
9936  if(Pos == 0)
9937  Third = Remainder;
9938  else
9939  {
9940  Third = Remainder.SubString(1, Pos - 1);
9941  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9942  }
9943  if((Second == "Snt") || (Second == "Snt-sh"))
9944  // third has to be 2 element idents with a space between
9945  {
9946  int SpacePos = Third.Pos(' ');
9947  if(SpacePos == 0)
9948  {
9949  Utilities->CallLogPop(919);
9950  return false; // no space
9951  }
9952  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
9953  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
9954  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
9955  if(CheckLocationsExistInRailway)
9956  {
9957  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
9958  {
9959  Utilities->CallLogPop(920);
9960  return false;
9961  }
9962  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
9963  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
9964  }
9965  if(Second == "Snt")
9966  {
9967  FormatType = StartNew;
9968  SequenceType = Start;
9969  LocationType = NoLocation;
9970  // can't be set until know whether located or not - done in SecondPassActions
9971  ShuttleLinkType = NotAShuttleLink;
9972  }
9973  else // Snt-sh
9974  {
9975  FormatType = SNTShuttle;
9976  LocationType = AtLocation;
9977  SequenceType = Start;
9978  ShuttleLinkType = ShuttleLink;
9979  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
9980  {
9981  Utilities->CallLogPop(1038);
9982  return false;
9983  }
9984  }
9985  Utilities->CallLogPop(921);
9986  return true;
9987  }
9988 
9989  if(Second == "Sns-sh") // third & fourth have to be headcodes
9990  {
9991  FormatType = SNSShuttle;
9992  LocationType = AtLocation;
9993  SequenceType = Start;
9994  ShuttleLinkType = ShuttleLink;
9995  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
9996  {
9997  Utilities->CallLogPop(1039);
9998  return false;
9999  }
10000  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
10001  {
10002  Utilities->CallLogPop(1040);
10003  return false;
10004  }
10005  Utilities->CallLogPop(1041);
10006  return true;
10007  }
10008 
10009  if(Second == "F-nshs")
10010  {
10011  FormatType = FNSNonRepeatToShuttle;
10012  LocationType = AtLocation;
10013  SequenceType = Finish;
10014  ShuttleLinkType = ShuttleLink;
10015  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
10016  {
10017  Utilities->CallLogPop(1047);
10018  return false;
10019  }
10020  Utilities->CallLogPop(1048);
10021  return true;
10022  }
10023 
10024  if(Second == "Sns-fsh")
10025  {
10026  FormatType = SNSNonRepeatFromShuttle;
10027  LocationType = AtLocation;
10028  SequenceType = Start;
10029  ShuttleLinkType = ShuttleLink;
10030  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
10031  {
10032  Utilities->CallLogPop(1098);
10033  return false;
10034  }
10035  Utilities->CallLogPop(1099);
10036  return true;
10037  }
10038 
10039  if(Second == "Fns-sh") // third & fourth have to be headcodes
10040  {
10041  FormatType = FSHNewService;
10042  LocationType = AtLocation;
10043  SequenceType = Finish;
10044  ShuttleLinkType = ShuttleLink;
10045  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
10046  {
10047  Utilities->CallLogPop(1050);
10048  return false;
10049  }
10050  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
10051  {
10052  Utilities->CallLogPop(1051);
10053  return false;
10054  }
10055  Utilities->CallLogPop(1052);
10056  return true;
10057  }
10058 
10059  // new segment for 'pas'
10060  if(Second == "pas") // third has to be a location
10061  {
10062  FormatType = PassTime;
10063  LocationType = EnRoute;
10064  SequenceType = Intermediate;
10065  ShuttleLinkType = NotAShuttleLink;
10066  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
10067  {
10068  Utilities->CallLogPop(1515);
10069  return false;
10070  }
10071  Utilities->CallLogPop(1516);
10072  return true;
10073  }
10074 
10075  // new segment for revised 'Fer'
10076  if(Second == "Fer")
10077  // third has to be a set of IDs separated by spaces, and at least 1
10078  {
10079  FormatType = ExitRailway;
10080  LocationType = EnRoute;
10081  SequenceType = Finish;
10082  ShuttleLinkType = NotAShuttleLink;
10083  if(CheckLocationsExistInRailway)
10084  {
10085  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
10086  {
10087  Utilities->CallLogPop(1519);
10088  return false;
10089  }
10090  }
10091  Utilities->CallLogPop(1520);
10092  return true;
10093  }
10094 
10095  // all remainder must be TimeCmdHeadCode types to be valid
10096  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
10097  (Second != "Frh-sh"))
10098  {
10099  Utilities->CallLogPop(922);
10100  return false; // all TimeCmdHeadCode types
10101  }
10102 
10103  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
10104  {
10105  Utilities->CallLogPop(923);
10106  return false;
10107  }
10108  FormatType = TimeCmdHeadCode;
10109  LocationType = AtLocation;
10110  if(Second == "Frh-sh")
10111  ShuttleLinkType = ShuttleLink;
10112  else
10113  ShuttleLinkType = NotAShuttleLink;
10114  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
10115  {
10116  SequenceType = Finish;
10117  }
10118  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
10119  {
10120  SequenceType = Intermediate;
10121  }
10122  if((Second == "Sfs") || (Second == "Sns"))
10123  {
10124  SequenceType = Start;
10125  }
10126  Utilities->CallLogPop(924);
10127  return true;
10128 }
10129 
10130 // ---------------------------------------------------------------------------
10131 
10132 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
10133 {
10134  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
10135  // and contains no special characters
10136  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
10137  if(LocStr == "")
10138  {
10139  Utilities->CallLogPop(1353);
10140  return false; // has to have at least one character
10141  }
10142  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
10143  {
10144  Utilities->CallLogPop(1354);
10145  return false; // can't begin with a number
10146  }
10147  for(int x = 1; x < LocStr.Length() + 1; x++)
10148  {
10149  if(LocStr[x] < ' ')
10150  {
10151  Utilities->CallLogPop(1355);
10152  return false; // contains a special character
10153  }
10154  if(LocStr[x] > 'z')
10155  {
10156  Utilities->CallLogPop(1356);
10157  return false; // contains a character outside the standard ASCII set
10158  }
10159  }
10160  // check exists in railway location list if CheckLocationsExistInRailway is true
10161  if(CheckLocationsExistInRailway)
10162  {
10163  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
10164  {
10165  TimetableMessage(GiveMessages, "Location name '" + LocStr +
10166  "' appears in the timetable but is not a valid name. To be valid the name must apply to one or more platforms (not concourses on their own), or to track at a blue non-station named location other than a continuation.");
10167  Utilities->CallLogPop(1357);
10168  return false;
10169  }
10170  }
10171  Utilities->CallLogPop(1358);
10172  return true;
10173 }
10174 
10175 // ---------------------------------------------------------------------------
10176 
10177 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
10178 {
10179  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
10180  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
10181  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
10182  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
10183  HeadCode);
10184  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
10185  {
10186  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
10187  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Snt-sh, Sns-sh, Fns-sh or Frh-sh");
10188  Utilities->CallLogPop(1359);
10189  return false;
10190  }
10191  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
10192  for(int x = 1; x < (HeadCode.Length() + 1); x++)
10193  {
10194  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
10195  {
10196  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
10197  Utilities->CallLogPop(1895);
10198  return false;
10199  }
10200  }
10201  // secondly ensure the true Headcode only has letters or digits
10202  for(int x = 3; x >= 0; x--)
10203  {
10204  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
10205  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
10206  {
10207  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
10208  Utilities->CallLogPop(1790);
10209  return false;
10210  }
10211  }
10212  Utilities->CallLogPop(1364);
10213  return true;
10214 }
10215 
10216 // ---------------------------------------------------------------------------
10217 
10218 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
10219  // set of legitimate track element IDs, separated by spaces, and at least 1 present
10220 {
10221  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
10222  ExitList.clear();
10223  AnsiString CurrentID = "";
10224 
10225  if(IDSet.Length() == 0)
10226  {
10227  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
10228  Utilities->CallLogPop(1521);
10229  return false;
10230  }
10231  for(int x = 1; x <= IDSet.Length(); x++)
10232  {
10233  char C = IDSet[x];
10234  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
10235  {
10236  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
10237  Utilities->CallLogPop(1522);
10238  return false;
10239  }
10240  }
10241  int Pos = IDSet.Pos(' '); // look for the first space
10242 
10243  while(true)
10244  {
10245  if(Pos == 0)
10246  {
10247  CurrentID = IDSet;
10248  IDSet = "";
10249  }
10250  else
10251  {
10252  CurrentID = IDSet.SubString(1, Pos - 1);
10253  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
10254  }
10255  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
10256  if(VecPos == -1)
10257  {
10258  Utilities->CallLogPop(1523);
10259  return false; // messages given in GetTrackVectorPositionFromString
10260  }
10261  else
10262  {
10263  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
10264  {
10265  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
10266  Utilities->CallLogPop(1524);
10267  return false;
10268  }
10269  else
10270  {
10271  // first check for duplicates
10272  if(!ExitList.empty())
10273  {
10274  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
10275  {
10276  if(*ELIT == VecPos)
10277  {
10278  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
10279  Utilities->CallLogPop(1532);
10280  return false;
10281  }
10282  }
10283  }
10284  // of OK add it to the list
10285  ExitList.push_back(VecPos);
10286  }
10287  }
10288  if(IDSet == "")
10289  {
10290  Utilities->CallLogPop(1525);
10291  return true;
10292  }
10293  else
10294  {
10295  Pos = IDSet.Pos(' '); // look for the next space
10296  }
10297  } // while(true)
10298 }
10299 
10300 // ---------------------------------------------------------------------------
10301 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
10302  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
10303  // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
10304  // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
10305  // of each item
10306 {
10307  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
10308  int Pos = 0;
10309  AnsiString Remainder = "";
10310  int SemiColonCount = 0;
10311 
10312  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
10313  {
10314  if(TrainInfoStr[x] == ';')
10315  SemiColonCount++;
10316  }
10317  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
10318  {
10319  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
10320  "'. Should be headcode + optional description for a continuing service;" +
10321  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
10322  Utilities->CallLogPop(880);
10323  return false;
10324  }
10325  if(SemiColonCount == 0)
10326  {
10327  HeadCode = TrainInfoStr;
10328  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
10329  {
10330  Utilities->CallLogPop(881);
10331  return false;
10332  }
10333  Utilities->CallLogPop(882);
10334  return true;
10335  }
10336  if(SemiColonCount == 1) // headcode & description only
10337  {
10338  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
10339  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
10340  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
10341  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
10342  {
10343  Utilities->CallLogPop(883);
10344  return false;
10345  }
10346  if(Description == "")
10347  {
10348  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
10349  Utilities->CallLogPop(884);
10350  return false;
10351  }
10352  if(Description.Length() > 60)
10353  {
10354  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
10355  Utilities->CallLogPop(1157);
10356  return false;
10357  }
10358  for(int x = 1; x < Description.Length() + 1; x++)
10359  {
10360  if((Description[x] < ' ') || (Description[x] > '~'))
10361  {
10362  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
10363  Utilities->CallLogPop(885);
10364  return false;
10365  }
10366  }
10367  Utilities->CallLogPop(886);
10368  return true;
10369  }
10370  // if here must have 6 or 7 semicolons
10371  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
10372  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
10373  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
10374  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
10375  {
10376  Utilities->CallLogPop(887);
10377  return false;
10378  }
10379  Pos = Remainder.Pos(';'); // 2nd delimiter
10380  Description = Remainder.SubString(1, Pos - 1);
10381  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10382  if(Description == "")
10383  {
10384  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
10385  Utilities->CallLogPop(888);
10386  return false;
10387  }
10388  if(Description.Length() > 60)
10389  {
10390  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
10391  Utilities->CallLogPop(1158);
10392  return false;
10393  }
10394  for(int x = 1; x < Description.Length() + 1; x++)
10395  {
10396  if((Description[x] < ' ') || (Description[x] > 126))
10397  {
10398  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
10399  Utilities->CallLogPop(889);
10400  return false;
10401  }
10402  }
10403  Pos = Remainder.Pos(';'); // 3rd delimiter
10404  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
10405 
10406  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10407  if(StartSpeedStr == "")
10408  {
10409  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
10410  Utilities->CallLogPop(890);
10411  return false;
10412  }
10413  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
10414  {
10415  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
10416  {
10417  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
10418  Utilities->CallLogPop(891);
10419  return false;
10420  }
10421  }
10422  StartSpeed = StartSpeedStr.ToInt();
10423  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
10424  {
10425  StartSpeed = TTrain::MaximumSpeedLimit;
10426  if(!SSHigh) // added at v2.4.0
10427  {
10428  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10429  SSHigh = true;
10430  }
10431  }
10432  Pos = Remainder.Pos(';'); // 4th delimiter
10433  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
10434 
10435  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10436  if(MaxRunningSpeedStr == "")
10437  {
10438  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
10439  Utilities->CallLogPop(892);
10440  return false;
10441  }
10442  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
10443  {
10444  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
10445  {
10446  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
10447  Utilities->CallLogPop(893);
10448  return false;
10449  }
10450  }
10451  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
10452  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
10453  {
10454  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
10455  if(!MRSHigh) // added at v2.4.0
10456  {
10457  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10458  MRSHigh = true;
10459  }
10460  }
10461  if(MaxRunningSpeed < 10)
10462  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
10463  {
10464  MaxRunningSpeed = 10;
10465  if(!MRSLow) // added at v2.4.0
10466  {
10467  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
10468  MRSLow = true;
10469  }
10470  }
10471  Pos = Remainder.Pos(';'); // 5th delimiter
10472  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
10473 
10474  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10475  if(MassStr == "")
10476  {
10477  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
10478  Utilities->CallLogPop(895);
10479  return false;
10480  }
10481  for(int x = 1; x < MassStr.Length() + 1; x++)
10482  {
10483  if((MassStr[x] < '0') || (MassStr[x] > '9'))
10484  {
10485  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
10486  Utilities->CallLogPop(896);
10487  return false;
10488  }
10489  }
10490  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
10491  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
10492  {
10493  Mass = TTrain::MaximumMassLimit;
10494  if(!MassHigh) // added at v2.4.0
10495  {
10496  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
10497  MassHigh = true;
10498  }
10499  }
10500  if(Mass == 0)
10501  {
10502  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
10503  Utilities->CallLogPop(897);
10504  return false;
10505  }
10506  Pos = Remainder.Pos(';'); // 6th delimiter
10507  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
10508 
10509  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10510  if(MaxBrakeForceStr == "")
10511  {
10512  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
10513  Utilities->CallLogPop(898);
10514  return false;
10515  }
10516  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
10517  {
10518  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
10519  {
10520  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
10521  Utilities->CallLogPop(899);
10522  return false;
10523  }
10524  }
10525  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
10526 
10527  // convert to kg force
10528  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
10529  {
10530  MaxBrakeForce = Mass;
10531  if(!BFHigh) // added at v2.4.0
10532  {
10533  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
10534  BFHigh = true;
10535  }
10536  }
10537  if((MaxBrakeForce / Mass) < 0.01)
10538  {
10539  MaxBrakeForce = Mass * 0.01;
10540  if(!BFLow) // added at v2.4.0
10541  {
10542  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
10543  BFLow = true;
10544  }
10545  }
10546  // convert to m/s/s
10547  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
10548  // now may have just a power entry or power and signaller max. speed
10549  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
10550 
10551  if(SemiColonCount == 6)
10552  {
10553  GrossPowerStr = Remainder;
10554  SignallerSpeedStr = "30"; // default value
10555  }
10556  else // must be 7
10557  {
10558  Pos = Remainder.Pos(';'); // 7th delimiter
10559  GrossPowerStr = Remainder.SubString(1, Pos - 1);
10560  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10561  }
10562  // deal with GrossPower
10563  if(GrossPowerStr == "")
10564  {
10565  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
10566  Utilities->CallLogPop(901);
10567  return false;
10568  }
10569  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
10570  {
10571  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
10572  {
10573  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
10574  Utilities->CallLogPop(902);
10575  return false;
10576  }
10577  }
10578 
10579  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
10580 
10581  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
10582  {
10583  GrossPower = TTrain::MaximumPowerLimit;
10584  if(!PwrHigh)
10585  {
10586  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
10587  PwrHigh = true;
10588  }
10589  }
10590  else if(GrossPower == 0) // changed at v2.4.0
10591  {
10592  GrossPower = 0.1;
10593  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
10594  }
10595  else if((GrossPower > 0) && (GrossPower < 10000))
10596  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
10597  {
10598  GrossPower = 10000;
10599  }
10600  PowerAtRail = GrossPower * 0.8;
10601  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
10602 
10603  // deal with SignallerSpeed
10604  if(SignallerSpeedStr == "")
10605  {
10606  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
10607  Utilities->CallLogPop(1771);
10608  return false;
10609  }
10610  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
10611  {
10612  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
10613  {
10614  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
10615  Utilities->CallLogPop(1769);
10616  return false;
10617  }
10618  }
10619  SignallerSpeed = SignallerSpeedStr.ToInt();
10620  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
10621  {
10622  SignallerSpeed = TTrain::MaximumSpeedLimit;
10623  if(!SigSHigh)
10624  {
10625  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10626  SigSHigh = true;
10627  }
10628  }
10629  if(SignallerSpeed < 10)
10630  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
10631  {
10632  SignallerSpeed = 10;
10633  if(!SigSLow)
10634  {
10635  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
10636  SigSLow = true;
10637  }
10638  // Utilities->CallLogPop(1770);
10639  // return false;
10640  }
10641  Utilities->CallLogPop(904);
10642  return true;
10643 }
10644 
10645 // ---------------------------------------------------------------------------
10646 
10647 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
10648  bool GiveMessages)
10649 {
10650  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
10651  // function checks validity of each item and returns false for error
10652  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
10653  if(OneEntry.Length() < 7)
10654  {
10655  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10656  Utilities->CallLogPop(865);
10657  return false;
10658  }
10659  int SemiColonCount = 0;
10660 
10661  for(int x = 1; x < OneEntry.Length() + 1; x++)
10662  {
10663  if(OneEntry[x] == ';')
10664  SemiColonCount++;
10665  }
10666  if(SemiColonCount != 3)
10667  {
10668  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10669  Utilities->CallLogPop(866);
10670  return false;
10671  }
10672  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
10673  {
10674  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10675  Utilities->CallLogPop(867);
10676  return false;
10677  }
10678  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
10679  // strip off R;
10680 
10681  int Pos = 0;
10682 
10683  Pos = Remainder.Pos(';');
10684  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
10685 
10686  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10687  if(MinutesStr == "")
10688  {
10689  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
10690  Utilities->CallLogPop(868);
10691  return false;
10692  }
10693  if(MinutesStr.Length() > 3)
10694  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
10695  {
10696  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
10697  Utilities->CallLogPop(2119);
10698  return false;
10699  }
10700  for(int x = 1; x < MinutesStr.Length() + 1; x++)
10701  {
10702  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
10703  {
10704  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
10705  Utilities->CallLogPop(869);
10706  return false;
10707  }
10708  }
10709  RearStartOrRepeatMins = MinutesStr.ToInt();
10710  if(RearStartOrRepeatMins == 0)
10711  {
10712  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
10713  Utilities->CallLogPop(870);
10714  return false;
10715  }
10716  Pos = Remainder.Pos(';');
10717  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
10718 
10719  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10720  if(DigitsStr == "")
10721  {
10722  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
10723  Utilities->CallLogPop(871);
10724  return false;
10725  }
10726  for(int x = 1; x < DigitsStr.Length() + 1; x++)
10727  {
10728  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
10729  {
10730  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
10731  Utilities->CallLogPop(872);
10732  return false;
10733  }
10734  }
10735  if(DigitsStr.Length() > 2)
10736  {
10737  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
10738  Utilities->CallLogPop(873);
10739  return false;
10740  }
10741  FrontStartOrRepeatDigits = DigitsStr.ToInt();
10742 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
10743  route rather than the service
10744  if(FrontStartOrRepeatDigits == 0)
10745  {
10746  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
10747  Utilities->CallLogPop(874);
10748  return false;
10749  }
10750 */
10751  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
10752  // new for v0.6b for unrestricted headcodes
10753  {
10754  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
10755  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
10756  Utilities->CallLogPop(1889);
10757  return false;
10758  }
10759  AnsiString NumberStr = Remainder;
10760 
10761  if(NumberStr == "")
10762  {
10763  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
10764  Utilities->CallLogPop(875);
10765  return false;
10766  }
10767  if(NumberStr.Length() > 4)
10768  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
10769  {
10770  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
10771  Utilities->CallLogPop(2118);
10772  return false;
10773  }
10774  for(int x = 1; x < NumberStr.Length() + 1; x++)
10775  {
10776  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
10777  // catches negative numbers
10778  {
10779  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
10780  Utilities->CallLogPop(876);
10781  return false;
10782  }
10783  }
10784  NumberOfRepeats = NumberStr.ToInt();
10785  if(NumberOfRepeats == 0)
10786  {
10787  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
10788  Utilities->CallLogPop(877);
10789  return false;
10790  }
10791  Utilities->CallLogPop(878);
10792  return true;
10793 }
10794 
10795 // ---------------------------------------------------------------------------
10796 
10797 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages)
10798 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
10799  vector rather than the timetable
10800  Note also that for unlocated Snt entries the LocationType hasn't yet been set
10801 
10802  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
10803 
10804  For info:-
10805  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
10806  {
10807  public:
10808  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled action entries, null
10810  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
10811  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
10812  int NumberOfRepeats; ///< the number of repeating services
10813  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
10815  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
10817  TExitList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
10818  TTimetableFormatType FormatType; ///< defines the timetable action type
10819  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
10820  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
10821  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
10822  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
10824  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
10826 
10827  // inline function
10828 
10830  TActionVectorEntry() {
10831  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
10832  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
10833  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
10834  Warning = false; SignallerControl = false;
10835  }
10836  };
10837 
10838  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
10839 
10840  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
10841  {
10842  public:
10843  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
10846  double MaxBrakeRate; ///< in metres/sec/sec
10847  double MaxRunningSpeed; ///< in km/h
10848  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
10849  int Mass; ///< in kg
10850  int NumberOfTrains; ///< number of repeats + 1
10851  int SignallerSpeed; ///< in km/h for use when under signaller control
10852  int StartSpeed; ///< in km/h
10853  TActionVector ActionVector; ///< all the actions for the train
10854  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
10855 
10856  //inline function
10857 
10859  TTrainDataEntry()
10860  {
10861  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
10862  }
10863  };
10864 
10865  Allowable successors:-
10866  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
10867  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
10868  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
10869  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10870  set location, else fails)
10871  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10872  set location, else fails)
10873  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10874  set location, else fails)
10875  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10876  set location, else fails)
10877  Fns -> R only
10878  F-nshs -> Nothing (no repeats permitted)
10879  Fjo -> R only
10880  Frh -> R only
10881  Fer -> R only
10882  Frh-sh -> R only
10883  Fns-sh -> R only
10884  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
10885  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10886  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10887  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10888  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10889  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10890  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10891  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10892  Repeat -> Nothing
10893 
10894  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
10895  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
10896  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
10897  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
10898  Check all times increase or stay same through ActionVector
10899  Cycle through all entries in vector setting arr & dep times based on above list
10900  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
10901  Check locations match the arr & dep TimeLoc entries
10902  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
10903  Make above valid succession checks
10904  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
10905  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
10906  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
10907  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
10908  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
10909  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
10910  Set train info for Sfs & Sns entries
10911  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
10912  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
10913  element at each end, or length of 3 & 1 extra element at either end
10914  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
10915  Check all Cmds have EventTime set & Arr & Dep times = -1
10916  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
10917  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
10918 
10919  Give messages in function if errors detected and clear the vector. Return false for failure.
10920 */
10921 
10922 /* Earlier checks:-
10923  Checks carried out with error messages in this function:-
10924  At least one comma in the line (it's based on a csv file);
10925  No entries following train information;
10926  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10927  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10928  First entry not a start entry;
10929  Train information incomplete before a start entry;
10930  Entry follows a finish entry but doesn't begin with 'R';
10931  SplitEntry returns false in a finish entry - message repeats the entry for information;
10932  Last action entry isn't a finish entry.
10933 
10934  Function returns false with no message if:-
10935  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10936  time is found at all then an error message is given in the calling function);
10937  SplitTrainInfo returns false (message given in called function);
10938  SplitRepeat returns false (message given in called function).
10939 */ {
10940  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
10941  if(TrainDataVector.empty())
10942  {
10943  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
10944  TrainDataVector.clear();
10945  Utilities->CallLogPop(1832);
10946  return false;
10947  }
10948 
10949 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
10950  1) must have at least one actionvector entry
10951  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
10952  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
10953  4) first entry must be a start;
10954  4a) if first entry is Snt and not signallercontrol and second is a finish then it can only be Frh, Fjo or Fer//added for v2.0.0
10955  5) a start must be the first entry;
10956  6) a repeat entry must be the last;
10957  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
10958  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
10959  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
10960 */
10961 
10962  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
10963  {
10964  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
10965  if(TrainDataVector.at(x).ActionVector.empty())
10966  {
10967  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
10968  TrainDataVector.clear();
10969  Utilities->CallLogPop(1833);
10970  return false;
10971  }
10972  }
10973  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
10974  {
10975  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
10976  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
10977  if(!(AVEntry0.SignallerControl))
10978  {
10979  if(TrainDataVector.at(x).ActionVector.size() == 1)
10980  {
10981  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
10982  TrainDataVector.clear();
10983  Utilities->CallLogPop(1822);
10984  return false;
10985  }
10986  }
10987  }
10988  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
10989  {
10990  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
10991  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
10992  if(AVEntry0.SignallerControl)
10993  {
10994  if(TrainDataVector.at(x).ActionVector.size() > 2)
10995  {
10996  SecondPassMessage(GiveMessages,
10997  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
10998  TDEntry.HeadCode);
10999  TrainDataVector.clear();
11000  Utilities->CallLogPop(1837);
11001  return false;
11002  }
11003  if(TrainDataVector.at(x).ActionVector.size() > 1)
11004  {
11005  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11006  if(AVEntry1.FormatType != Repeat)
11007  {
11008  SecondPassMessage(GiveMessages,
11009  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
11010  TrainDataVector.clear();
11011  Utilities->CallLogPop(1838);
11012  return false;
11013  }
11014  }
11015  }
11016  }
11017  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
11018  {
11019  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11020  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11021  if(AVEntry0.SequenceType != Start)
11022  {
11023  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
11024  TrainDataVector.clear();
11025  Utilities->CallLogPop(1824);
11026  return false;
11027  }
11028  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
11029  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
11030  // and others for a located Snt, but those checks done later
11031  {
11032  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11033  // must be a second entry if first not signallercontrol
11034  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
11035  {
11036  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
11037  TDEntry.HeadCode);
11038  TrainDataVector.clear();
11039  Utilities->CallLogPop(2046);
11040  return false;
11041  }
11042  }
11043  }
11044  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
11045  {
11046  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11047  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11048  {
11049  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11050  if((AVEntry.SequenceType == Start) && (y != 0))
11051  {
11052  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
11053  TrainDataVector.clear();
11054  Utilities->CallLogPop(1825);
11055  return false;
11056  }
11057  }
11058  }
11059  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
11060  {
11061  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11062  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11063  {
11064  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11065  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
11066  {
11067  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
11068  TrainDataVector.clear();
11069  Utilities->CallLogPop(1826);
11070  return false;
11071  }
11072  }
11073  }
11074  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
11075  {
11076  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11077  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11078  {
11079  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11080  if((y == 0) && AVEntry.SignallerControl)
11081  {
11082  break;
11083  }
11084  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
11085  {
11086  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
11087  {
11088  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
11089  TrainDataVector.clear();
11090  Utilities->CallLogPop(1827);
11091  return false;
11092  }
11093  if(AVEntry.FormatType == Repeat)
11094  {
11095  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
11096  if(LastAVEntry.SequenceType != Finish)
11097  {
11098  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
11099  TrainDataVector.clear();
11100  Utilities->CallLogPop(1828);
11101  return false;
11102  }
11103  }
11104  }
11105  }
11106  }
11107  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
11108  {
11109  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11110  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11111  {
11112  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11113  if(AVEntry.SequenceType == Finish)
11114  {
11115  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
11116  {
11117  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
11118  TrainDataVector.clear();
11119  Utilities->CallLogPop(1829);
11120  return false;
11121  }
11122  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
11123  {
11124  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11125  {
11126  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
11127  TDEntry.HeadCode);
11128  TrainDataVector.clear();
11129  Utilities->CallLogPop(1830);
11130  return false;
11131  }
11132  }
11133  }
11134  }
11135  }
11136 
11137  // end of new preliminary checks
11138 
11139  // check ActionVector present and check start event successor validity
11140  // For Snt & Snt-sh set location if stopped, don't set any times yet
11141  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11142  {
11143  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11144  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11145  // use reference so can change internals where necessary
11146  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
11147  {
11148  AnsiString LocationName = "";
11149  if(IsSNTEntryLocated(0, TDEntry, LocationName))
11150  // it is at a location
11151  {
11152  if(TDEntry.StartSpeed == 0) // stopped
11153  {
11154  AVEntry0.LocationName = LocationName;
11155  AVEntry0.LocationType = AtLocation;
11156  // check successor validity for located Snt that isn't a SignallerControl entry
11157  if(!AVEntry0.SignallerControl)
11158  {
11159  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11160  // at least 2 entries present checked in integrity check so (1) valid
11161  if(!AtLocSuccessor(AVEntry1))
11162  {
11163  // Frh following Snt-sh will return false in location check, so no need to check here
11164  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
11165  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11166  TrainDataVector.clear();
11167  Utilities->CallLogPop(523);
11168  return false;
11169  }
11170  }
11171  }
11172  else
11173  {
11174  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
11175  TDEntry.HeadCode);
11176  TrainDataVector.clear();
11177  Utilities->CallLogPop(791);
11178  return false;
11179  }
11180  }
11181  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
11182  {
11183  if(AVEntry0.Command == "Snt-sh")
11184  {
11185  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
11186  TrainDataVector.clear();
11187  Utilities->CallLogPop(1042);
11188  return false;
11189  }
11190  AVEntry0.LocationType = EnRoute;
11191  if(!AVEntry0.SignallerControl)
11192  {
11193  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11194  // at least 2 entries checked in integrity check so (1) valid
11195  if(!MovingSuccessor(AVEntry1))
11196  {
11197  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
11198  TDEntry.HeadCode);
11199  TrainDataVector.clear();
11200  Utilities->CallLogPop(790);
11201  return false;
11202  }
11203  }
11204  }
11205  }
11206  // check other start successors
11207  else if(AVEntry0.SequenceType == Start)
11208  {
11209  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11210  // at least 2 entries present checked in integrity check so (1) valid
11211  if(!AtLocSuccessor(AVEntry1))
11212  {
11213  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
11214  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11215  TrainDataVector.clear();
11216  Utilities->CallLogPop(793);
11217  return false;
11218  }
11219  }
11220  }
11221 
11222  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
11223  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11224  {
11225  bool FoundFlag = false;
11226  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11227  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
11228  // use reference so can change internals
11229  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
11230  {
11231  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
11232  {
11233  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
11234  if(AVEntry2.FormatType == TimeLoc)
11235  {
11236  FoundFlag = true;
11237  AVEntry.LocationName = AVEntry2.LocationName;
11238  break;
11239  }
11240  }
11241  if(!FoundFlag)
11242  {
11243  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
11244  TDEntry.HeadCode);
11245  TrainDataVector.clear();
11246  Utilities->CallLogPop(851);
11247  return false;
11248  }
11249  }
11250  }
11251 
11252  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
11253  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11254  {
11255  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11256  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11257  {
11258  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11259  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
11260  {
11261  if(AVEntry.LocationName == "")
11262  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
11263  {
11264  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
11265  ": an event should have had a location name associated with it but it could not be found");
11266  TrainDataVector.clear();
11267  Utilities->CallLogPop(1831);
11268  return false;
11269  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
11270  }
11271  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
11272  {
11273  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
11274  // use reference so can change internals where necessary
11275  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
11276  {
11277  AVEntry2.LocationName = AVEntry.LocationName;
11278  }
11279  else
11280  break;
11281  }
11282  }
11283  }
11284  }
11285  // all location names now set
11286 
11287  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
11288  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11289  {
11290  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11291  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11292  {
11293  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11294  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
11295  {
11296  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
11297  // i.e at least one more, must be a repeat
11298  {
11299  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11300  {
11301  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
11302  TrainDataVector.clear();
11303  Utilities->CallLogPop(798);
11304  return false;
11305  }
11306  }
11307  }
11308  if(AVEntry.Command == "F-nshs")
11309  {
11310  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
11311  // i.e has to be the last
11312  {
11313  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
11314  TrainDataVector.clear();
11315  Utilities->CallLogPop(1049);
11316  return false;
11317  }
11318  }
11319  if(AVEntry.Command == "pas")
11320  {
11321  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11322  {
11323  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
11324  TrainDataVector.clear();
11325  Utilities->CallLogPop(1518);
11326  return false;
11327  }
11328  }
11329  if(AVEntry.Command == "jbo")
11330  {
11331  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11332  {
11333  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
11334  TrainDataVector.clear();
11335  Utilities->CallLogPop(800);
11336  return false;
11337  }
11338  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11339  if(!AtLocSuccessor(AVEntry2))
11340  {
11341  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
11342  ". The event isn't valid for a stationary train.");
11343  TrainDataVector.clear();
11344  Utilities->CallLogPop(801);
11345  return false;
11346  }
11347  }
11348  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
11349  {
11350  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11351  {
11352  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
11353  TrainDataVector.clear();
11354  Utilities->CallLogPop(802);
11355  return false;
11356  }
11357  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11358  if(!AtLocSuccessor(AVEntry2))
11359  {
11360  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
11361  ". The event isn't valid for a stationary train.");
11362  TrainDataVector.clear();
11363  Utilities->CallLogPop(803);
11364  return false;
11365  }
11366  }
11367  if(AVEntry.Command == "cdt")
11368  {
11369  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11370  {
11371  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
11372  TrainDataVector.clear();
11373  Utilities->CallLogPop(804);
11374  return false;
11375  }
11376  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11377  if(!AtLocSuccessor(AVEntry2))
11378  {
11379  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
11380  ". The event isn't valid for a stationary train.");
11381  TrainDataVector.clear();
11382  Utilities->CallLogPop(805);
11383  return false;
11384  }
11385  }
11386  if(AVEntry.FormatType == TimeTimeLoc)
11387  {
11388  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11389  {
11390  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
11391  TrainDataVector.clear();
11392  Utilities->CallLogPop(806);
11393  return false;
11394  }
11395  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11396  if(!MovingSuccessor(AVEntry2))
11397  {
11398  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
11399  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
11400  TrainDataVector.clear();
11401  Utilities->CallLogPop(807);
11402  return false;
11403  }
11404  }
11405  if(AVEntry.FormatType == PassTime)
11406  {
11407  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11408  {
11409  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
11410  TrainDataVector.clear();
11411  Utilities->CallLogPop(1530);
11412  return false;
11413  }
11414  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11415  if(!MovingSuccessor(AVEntry2))
11416  {
11417  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
11418  ". The event isn't valid for a moving train.");
11419  TrainDataVector.clear();
11420  Utilities->CallLogPop(1531);
11421  return false;
11422  }
11423  }
11424  if(AVEntry.FormatType == Repeat)
11425  {
11426  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
11427  {
11428  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
11429  TrainDataVector.clear();
11430  Utilities->CallLogPop(808);
11431  return false;
11432  }
11433  }
11434  }
11435  }
11436 
11437  // set arrival & departure times for TimeLocs & set their EventTimes to -1
11438  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11439  {
11440  bool LastEntryIsAnArrival = false;
11441  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
11442  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
11443  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11444  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
11445  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
11446  {
11447  LastEntryIsAnArrival = false;
11448  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11449  {
11450  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11451  if(AVEntry.FormatType == TimeLoc)
11452  {
11453  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
11454  {
11455  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
11456  }
11457  if(LastEntryIsAnArrival)
11458  {
11459  AVEntry.DepartureTime = AVEntry.EventTime;
11460  AVEntry.EventTime = TDateTime(-1);
11461  LastEntryIsAnArrival = false;
11462  }
11463  else // last entry a departure
11464  {
11465  AVEntry.ArrivalTime = AVEntry.EventTime;
11466  AVEntry.EventTime = TDateTime(-1);
11467  LastEntryIsAnArrival = true;
11468  }
11469  }
11470  }
11471  }
11472  else // all others stopped at beginning
11473  {
11474  LastEntryIsAnArrival = true;
11475  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11476  {
11477  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11478  if(AVEntry.FormatType == TimeLoc)
11479  {
11480  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
11481  {
11482  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
11483  }
11484  if(LastEntryIsAnArrival)
11485  {
11486  AVEntry.DepartureTime = AVEntry.EventTime;
11487  AVEntry.EventTime = TDateTime(-1);
11488  LastEntryIsAnArrival = false;
11489  }
11490  else // last entry a departure
11491  {
11492  AVEntry.ArrivalTime = AVEntry.EventTime;
11493  AVEntry.EventTime = TDateTime(-1);
11494  LastEntryIsAnArrival = true;
11495  }
11496  }
11497  }
11498  }
11499  }
11500  // perform remaining successor checks for TimeLocs
11501  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11502  {
11503  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11504  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11505  {
11506  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11507  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
11508  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
11509  {
11510  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11511  {
11512  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
11513  TrainDataVector.clear();
11514  Utilities->CallLogPop(809);
11515  return false;
11516  }
11517  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11518  if(!AtLocSuccessor(AVEntry2))
11519  {
11520  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
11521  ". The event isn't valid for a stationary train.");
11522  TrainDataVector.clear();
11523  Utilities->CallLogPop(810);
11524  return false;
11525  }
11526  }
11527  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
11528  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
11529  {
11530  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11531  {
11532  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
11533  TrainDataVector.clear();
11534  Utilities->CallLogPop(811);
11535  return false;
11536  }
11537  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11538  if(!MovingSuccessor(AVEntry2))
11539  {
11540  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
11541  ". The event isn't valid for a moving train.");
11542  TrainDataVector.clear();
11543  Utilities->CallLogPop(812);
11544  return false;
11545  }
11546  }
11547  }
11548  }
11549 
11550  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
11551  // & repeats have no times set
11552  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11553  {
11554  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11555  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11556  {
11557  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11558  if(AVEntry.FormatType == TimeLoc)
11559  {
11560  if(AVEntry.EventTime != TDateTime(-1))
11561  {
11562  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
11563  }
11564  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
11565  {
11566  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
11567  }
11568  }
11569  if(AVEntry.FormatType == TimeTimeLoc)
11570  {
11571  if(AVEntry.EventTime != TDateTime(-1))
11572  {
11573  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
11574  }
11575  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
11576  {
11577  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
11578  }
11579  }
11580  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
11581  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
11582  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
11583  {
11584  if(AVEntry.EventTime == TDateTime(-1))
11585  {
11586  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
11587  }
11588  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
11589  {
11590  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
11591  }
11592  }
11593  if(AVEntry.FormatType == Repeat)
11594  {
11595  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
11596  {
11597  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
11598  }
11599  }
11600  }
11601  }
11602 
11603  // check times stay same or increase, note that can have time of 0 if include midnight
11604  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11605  {
11606  TDateTime CurrentTime = TTClockTime; // the timetable start time
11607  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11608  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11609  {
11610  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11611  if(AVEntry.FormatType == Repeat)
11612  break;
11613  if(AVEntry.FormatType == FinRemHere)
11614  break;
11615  if(AVEntry.FormatType == TimeTimeLoc)
11616  {
11617  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
11618  {
11619  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
11620  TDEntry.HeadCode);
11621  TrainDataVector.clear();
11622  Utilities->CallLogPop(813);
11623  return false;
11624  }
11625  if(AVEntry.ArrivalTime < CurrentTime)
11626  {
11627  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
11628  TDEntry.HeadCode);
11629  TrainDataVector.clear();
11630  Utilities->CallLogPop(814);
11631  return false;
11632  }
11633  CurrentTime = AVEntry.DepartureTime;
11634  continue;
11635  }
11636  if(AVEntry.FormatType == TimeLoc)
11637  {
11638  if(AVEntry.ArrivalTime >= TDateTime(0))
11639  {
11640  if(AVEntry.ArrivalTime < CurrentTime)
11641  {
11642  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
11643  TrainDataVector.clear();
11644  Utilities->CallLogPop(815);
11645  return false;
11646  }
11647  CurrentTime = AVEntry.ArrivalTime;
11648  }
11649  else
11650  {
11651  if(AVEntry.DepartureTime < CurrentTime)
11652  // both may be 0 legitimately so must allow for this
11653  {
11654  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
11655  TrainDataVector.clear();
11656  Utilities->CallLogPop(816);
11657  return false;
11658  }
11659  CurrentTime = AVEntry.DepartureTime;
11660  }
11661  continue;
11662  }
11663  if(AVEntry.EventTime < CurrentTime)
11664  // all others have EventTime set
11665  {
11666  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
11667  ", may be before timetable start time");
11668  TrainDataVector.clear();
11669  Utilities->CallLogPop(835);
11670  return false;
11671  }
11672  CurrentTime = AVEntry.EventTime;
11673  continue;
11674  }
11675  }
11676 
11677  // check locations consistent
11678  AnsiString LastLocationName = "";
11679 
11680  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11681  {
11682  bool LastEntryIsAnArrival = false;
11683  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11684  // first deal with moving Snt entries (all else stopped)
11685  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
11686  {
11687  LastEntryIsAnArrival = false;
11688  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
11689  if(LastLocationName != "")
11690  {
11691  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
11692  }
11693  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
11694  y++) // note that immediate successor to a moving Snt can only be a Moving type
11695  // if it's a SignallerControl entry then the condition isn't met
11696  {
11697  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11698  if(AVEntry.FormatType == Repeat)
11699  break; // repeat = reached end (+allows repeat after signaller controlled entry)
11700  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
11701  {
11702  if(AVEntry.LocationName != LastLocationName)
11703  {
11704  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11705  AVEntry.Command);
11706  TrainDataVector.clear();
11707  Utilities->CallLogPop(823);
11708  return false;
11709  }
11710  }
11711  else if(AVEntry.FormatType == TimeCmd)
11712  // cdt is the only TimeCmd
11713  {
11714  if(AVEntry.LocationName != LastLocationName)
11715  {
11716  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11717  AVEntry.Command);
11718  TrainDataVector.clear();
11719  Utilities->CallLogPop(824);
11720  return false;
11721  }
11722  }
11723  else if(AVEntry.FormatType == TimeTimeLoc)
11724  {
11725  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11726  // last entry must be a departure or would have failed earlier
11727  {
11728  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11730 // SecondPassMessage(GiveMessages,
11731 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
11732 // TrainDataVector.clear();
11733 // Utilities->CallLogPop(825);
11734 // return false;
11735  }
11736  LastLocationName = AVEntry.LocationName;
11737  LastEntryIsAnArrival = false;
11738  }
11739  else if(AVEntry.FormatType == TimeLoc)
11740  {
11741  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
11742  {
11743  SecondPassMessage(GiveMessages,
11744  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
11745  TrainDataVector.clear();
11746  Utilities->CallLogPop(826);
11747  return false;
11748  }
11749  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
11750  {
11751  SecondPassMessage(GiveMessages,
11752  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
11753  TrainDataVector.clear();
11754  Utilities->CallLogPop(827);
11755  return false;
11756  }
11757  LastLocationName = AVEntry.LocationName;
11758  LastEntryIsAnArrival = !LastEntryIsAnArrival;
11759  }
11760  }
11761  }
11762  else // all stationary starting entries
11763  {
11764  LastEntryIsAnArrival = true;
11765  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
11766  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11767  {
11768  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11769  if(AVEntry.FormatType == Repeat)
11770  break;
11771  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
11772  // no need to add anything for shuttle starts since they are at loc (0) anyway
11773  {
11774  if(AVEntry.LocationName != LastLocationName)
11775  {
11776  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11777  AVEntry.Command);
11778  TrainDataVector.clear();
11779  Utilities->CallLogPop(828);
11780  return false;
11781  }
11782  }
11783  else if(AVEntry.FormatType == TimeCmd)
11784  // cdt is the only TimeCmd
11785  {
11786  if(AVEntry.LocationName != LastLocationName)
11787  {
11788  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11789  AVEntry.Command);
11790  TrainDataVector.clear();
11791  Utilities->CallLogPop(829);
11792  return false;
11793  }
11794  }
11795  else if(AVEntry.FormatType == TimeTimeLoc)
11796  {
11797  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11798  // last entry must be a departure or would have failed earlier
11799  {
11800  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11802 // SecondPassMessage(GiveMessages,
11803 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
11804 // TrainDataVector.clear();
11805 // Utilities->CallLogPop(830);
11806 // return false;
11807  }
11808  LastLocationName = AVEntry.LocationName;
11809  LastEntryIsAnArrival = false;
11810  }
11811  else if(AVEntry.FormatType == TimeLoc)
11812  {
11813  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
11814  {
11815  SecondPassMessage(GiveMessages,
11816  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
11817  TrainDataVector.clear();
11818  Utilities->CallLogPop(831);
11819  return false;
11820  }
11821  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
11822  {
11823  SecondPassMessage(GiveMessages,
11824  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
11826 // TrainDataVector.clear();
11827 // Utilities->CallLogPop(832);
11828 // return false;
11829  }
11830  LastLocationName = AVEntry.LocationName;
11831  LastEntryIsAnArrival = !LastEntryIsAnArrival;
11832  }
11833  }
11834  }
11835  }
11836 
11837  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
11838  // i.e. same location can appear in any number of consecutive entries but once changed can't repeat
11839  // before a direction change
11840  AnsiString LocationNameToBeChecked = "";
11841 
11842  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11843  {
11844  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
11845  unsigned int y = 0;
11846  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
11847  // first discard unlocated Snt entries as they don't have location name set
11848  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
11849  {
11850  y = 1;
11851  }
11852  while(y < TDEntry.ActionVector.size())
11853  // need to check each location name separately in turn, skipped for SignallerControl entries
11854  // if y == 1
11855  {
11856  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
11857  {
11858  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
11859  }
11860  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
11861  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
11862  {
11863  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
11864  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
11865  {
11866  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
11867  }
11868  if(AVEntry.Command == "cdt")
11869  {
11870  break; // out of the 'z' loop since the check is only valid up to a change of direction
11871  }
11872  if(AVEntry.LocationName == LocationNameToBeChecked)
11873  continue; // keep going while name same
11874  if(AVEntry.LocationName != LocationNameToBeChecked)
11875  // if name different check forwards to see if repeats
11876  {
11877  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
11878  {
11879  if(TDEntry.ActionVector.at(a).Command == "cdt")
11880  {
11881  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
11882  }
11883  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11884  {
11885  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11887  // SecondPassMessage(GiveMessages, "Error in timetable - a location entry appears twice inappropriately for: " + TDEntry.HeadCode);
11888  // TrainDataVector.clear();
11889  // Utilities->CallLogPop(833);
11890  // return false;
11891  }
11892  }
11893  break; // out of the 'z' loop since have checked 'a' as far as need to
11894  }
11895  }
11896  y++;
11897  }
11898  }
11899 
11900  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
11901  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11902  {
11903  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11904  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11905  {
11906  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11907  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
11908  {
11909  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
11910  }
11911  AnsiString LocName = "";
11912  // dummy, only used so can call IsSNTEntryLocated
11913  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
11914  {
11915  if(AVEntry.LocationName == "")
11916  {
11917  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
11918  }
11919  }
11920  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
11921  {
11922  if(AVEntry.LocationName != "")
11923  {
11924  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
11925  }
11926  }
11927  }
11928  }
11929 
11930 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11931  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11932  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
11933 
11934  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
11935  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
11936 
11937  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
11938  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
11939  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
11940 */
11941  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
11942  // non-shuttles & non-repeating links separately, but don't check that there isn't a
11943  // duplicate between a non-repeating shuttle and another - leave original tests in as
11944  // these also set the pointers
11945  {
11946  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11947  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11948  {
11949  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11950  if(AVEntry.OtherHeadCode != "")
11951  {
11952  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
11953  {
11954  Utilities->CallLogPop(1584);
11955  return false; // error message given in called function
11956  }
11957  }
11958  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
11959  {
11960  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
11961  {
11962  Utilities->CallLogPop(1585);
11963  return false; // error message given in called function
11964  }
11965  }
11966  }
11967  }
11968 
11969  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11970  {
11971  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11972  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11973  {
11974  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11975  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
11976  {
11977  if(AVEntry.OtherHeadCode != "")
11978  {
11979  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
11980  // false = non-shuttle
11981  {
11982  Utilities->CallLogPop(864);
11983  return false; // error message given in called function
11984  }
11985  }
11986  }
11987  }
11988  }
11989 
11990  // now repeat the check just for the shuttles
11991  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11992  {
11993  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11994  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11995  {
11996  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11997  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
11998  {
11999  if(AVEntry.OtherHeadCode != "")
12000  {
12001  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
12002  // true = shuttle
12003  {
12004  Utilities->CallLogPop(1100);
12005  return false; // error message given in called function
12006  }
12007  }
12008  }
12009  }
12010  }
12011 
12012  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
12013  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12014  {
12015  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12016  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12017  {
12018  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12019  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12020  {
12022  {
12023  Utilities->CallLogPop(1060);
12024  return false; // error message given in called function
12025  }
12026  }
12027  }
12028  }
12029 
12030  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
12031  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
12032  // don't ever need to and as designed would skip repeats
12033  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12034  {
12035  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12036  {
12037  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12038  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
12039  {
12040  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
12041  {
12042  Utilities->CallLogPop(1090);
12043  return false; // error message given in called function
12044  }
12045  }
12046  }
12047  }
12048 
12049  // check all entries have all types set to something
12050  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12051  {
12052  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12053  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12054  {
12055  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12056  if(AVEntry.FormatType == NoFormat)
12057  {
12058  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
12059  }
12060  else if(AVEntry.SequenceType == NoSequence)
12061  {
12062  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
12063  }
12064  else if(AVEntry.LocationType == NoLocation)
12065  {
12066  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
12067  }
12068  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
12069  {
12070  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
12071  }
12072  }
12073  }
12074 
12075  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
12076  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12077  {
12078  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12079  // non-const reference so can alter content
12080  TTrainOperatingData TData;
12081  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
12082  if(LastAVEntry.FormatType == Repeat) // check if a repeat
12083  {
12084 /*
12085  class TTrainOperatingData
12086  {
12087  public:
12088  int TrainID; - default, set at construction
12089  TActionEventType EventReported; used during operation
12090  TRunningEntry RunningEntry; - default, set at construction
12091  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
12092  };
12093 */
12094  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
12095  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
12096  {
12097  TDEntry.TrainOperatingDataVector.push_back(TData);
12098  }
12099  }
12100  else
12101  TDEntry.NumberOfTrains = 1;
12102  }
12103 
12104  // check that don't include any Continuation names
12105  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12106  {
12107  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12108  {
12109  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
12110  AnsiString HC = TrainDataVector.at(x).HeadCode;
12111  if(LocName != "")
12112  {
12113  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
12114  {
12115  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
12116  TrainDataVector.clear();
12117  Utilities->CallLogPop(1578);
12118  return false;
12119  }
12120  }
12121  }
12122  }
12123 
12124  // check that all repeat times below 96h
12125  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12126  {
12127  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
12128  int IncMinutes = 0;
12129  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
12130  {
12131  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
12132  }
12133  else
12134  continue; // basic times already checked in CheckTimeValidity
12135  AnsiString HC = TrainDataVector.at(x).HeadCode;
12136  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12137  {
12138  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
12139  {
12140  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12141  {
12142  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
12143  TrainDataVector.clear();
12144  Utilities->CallLogPop(1818);
12145  return false;
12146  }
12147  }
12148  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
12149  {
12150  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12151  // 3d 23h 59m = 3.9993055556
12152  {
12153  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12154  TrainDataVector.clear();
12155  Utilities->CallLogPop(1819);
12156  return false;
12157  }
12158  }
12159  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
12160  {
12161  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12162  // 3d 23h 59m = 3.9993055556
12163  {
12164  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12165  TrainDataVector.clear();
12166  Utilities->CallLogPop(1820);
12167  return false;
12168  }
12169  }
12170  }
12171  }
12172 
12173  // Now that all set up change any extended headcodes back to ordinary headcodes
12174  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12175  {
12176  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
12177  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12178  {
12179  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
12180  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
12181  }
12182  }
12183 
12184  // SaveTrainDataVectorToFile(0);//test
12186  Utilities->CallLogPop(782);
12187  return true;
12188 }
12189 
12190 // ---------------------------------------------------------------------------
12191 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
12193 {
12194  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
12195 }
12196 
12197 // ---------------------------------------------------------------------------
12198 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
12200 {
12201  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
12202  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
12203  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
12204 }
12205 
12206 // ---------------------------------------------------------------------------
12207 
12208 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
12209 {
12210  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
12211  if(HeadCode.Length() > 4) // ignore otherwise
12212  {
12213  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
12214  }
12215  Utilities->CallLogPop(1593);
12216 }
12217 
12218 // ---------------------------------------------------------------------------
12219 
12220 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
12221 {
12222  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
12223  SecondHeadCode);
12224  int ForwardCount = 0;
12225  int ReverseCount = 0;
12226 
12227  if(MainHeadCode == SecondHeadCode)
12228  {
12229  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
12230  TrainDataVector.clear();
12231  Utilities->CallLogPop(1594);
12232  return false;
12233  }
12234 
12235  // forward check
12236  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12237  {
12238  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12239  if(TDEntry.HeadCode == MainHeadCode)
12240  {
12241  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12242  {
12243  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12244  if(AVEntry.OtherHeadCode == SecondHeadCode)
12245  {
12246  ForwardCount++;
12247  }
12248  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
12249  // need own check in case both 'Other' & 'NonRepeating' have same headcode
12250  {
12251  ForwardCount++;
12252  }
12253  }
12254  }
12255  }
12256  if(ForwardCount == 0)
12257  // this is an exception because the headcodes are selected in the same order as the forward check
12258  {
12259  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
12260  }
12261 
12262  if(ForwardCount > 2)
12263  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
12264  {
12265  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
12266  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
12267  TrainDataVector.clear();
12268  Utilities->CallLogPop(1587);
12269  return false;
12270  }
12271 
12272  // reverse check
12273  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12274  {
12275  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12276  if(TDEntry.HeadCode == SecondHeadCode)
12277  {
12278  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12279  {
12280  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12281  if(AVEntry.OtherHeadCode == MainHeadCode)
12282  {
12283  ReverseCount++;
12284  }
12285  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
12286  {
12287  ReverseCount++;
12288  }
12289  }
12290  }
12291  }
12292 
12293  if(ReverseCount == 0)
12294  {
12295  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
12296  TrainDataVector.clear();
12297  Utilities->CallLogPop(1588);
12298  return false;
12299  }
12300  if(ReverseCount > 2)
12301  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
12302  {
12303  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
12304  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
12305  TrainDataVector.clear();
12306  Utilities->CallLogPop(1589);
12307  return false;
12308  }
12309 
12310  if(ForwardCount != ReverseCount)
12311  {
12312  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
12313  " than the other way round");
12314  TrainDataVector.clear();
12315  Utilities->CallLogPop(1610);
12316  return false;
12317  }
12318 
12319  Utilities->CallLogPop(1590);
12320  return true;
12321 }
12322 
12323 // ---------------------------------------------------------------------------
12324 
12325 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
12326 /* Return false for no find or more than one find, check correct types of link
12327  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
12328  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
12329  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
12330  Then do the same in reverse.
12331  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
12332  if main is Fns other must be Sns; if main is jbo other must be Fjo.
12333  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
12334  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
12335  for Sfs & Sns services. Finally check the repeat entries if present are consistent
12336 
12337  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
12338  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
12339 
12340  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
12341 */
12342 
12343 {
12344  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
12345  int ForwardCount = 0;
12346  int ReverseCount = 0;
12347  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
12348  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
12349  TTrainDataEntry *MainTrainDataPtr = 0;
12350  TTrainDataEntry *OtherTrainDataPtr = 0;
12351 
12352  // forward check
12353  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12354  {
12355  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12356  if(TDEntry.HeadCode == MainHeadCode)
12357  {
12358  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12359  {
12360  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12361  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12362  {
12363  if(AVEntry.OtherHeadCode == OtherHeadCode)
12364  {
12365  MainTrainDataPtr = &TrainDataVector.at(x);
12366  ForwardEntryPtr = &AVEntry;
12367  ForwardCount++;
12368  ForwardTDVectorNumber = x;
12369  }
12370  }
12371  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
12372  (AVEntry.Command == "Frh-sh")))
12373  {
12374  if(AVEntry.OtherHeadCode == OtherHeadCode)
12375  {
12376  MainTrainDataPtr = &TrainDataVector.at(x);
12377  ForwardEntryPtr = &AVEntry;
12378  ForwardCount++;
12379  ForwardTDVectorNumber = x;
12380  }
12381  }
12382  }
12383  }
12384  }
12385  if(ForwardCount == 0)
12386  // this is an exception because the headcodes are selected in the same order as the forward check
12387  {
12388  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
12389  }
12390  if(ForwardCount > 1)
12391  {
12392  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
12393  MainHeadCode);
12394  TrainDataVector.clear();
12395  Utilities->CallLogPop(836);
12396  return false;
12397  }
12398 
12399  // reverse check
12400  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12401  {
12402  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12403  if(TDEntry.HeadCode == OtherHeadCode)
12404  {
12405  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12406  {
12407  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12408  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12409  {
12410  if(AVEntry.OtherHeadCode == MainHeadCode)
12411  {
12412  OtherTrainDataPtr = &TrainDataVector.at(x);
12413  ReverseCount++;
12414  ReverseEntryPtr = &AVEntry;
12415  ReverseTDVectorNumber = x;
12416  }
12417  }
12418  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
12419  {
12420  if(AVEntry.OtherHeadCode == MainHeadCode)
12421  {
12422  OtherTrainDataPtr = &TrainDataVector.at(x);
12423  ReverseCount++;
12424  ReverseEntryPtr = &AVEntry;
12425  ReverseTDVectorNumber = x;
12426  }
12427  }
12428  }
12429  }
12430  }
12431 
12432  if(ReverseCount == 0)
12433  {
12434  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
12435  TrainDataVector.clear();
12436  Utilities->CallLogPop(837);
12437  return false;
12438  }
12439  if(ReverseCount > 1)
12440  {
12441  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
12442  OtherHeadCode);
12443  TrainDataVector.clear();
12444  Utilities->CallLogPop(838);
12445  return false;
12446  }
12447 
12448  // these will all be false for !Shuttle
12449  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
12450  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
12451  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
12452  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
12453 
12454  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
12455  {
12456  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
12457  TrainDataVector.clear();
12458  Utilities->CallLogPop(1058);
12459  return false;
12460  }
12461 
12462  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
12463  {
12464  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
12465  TrainDataVector.clear();
12466  Utilities->CallLogPop(1059);
12467  return false;
12468  }
12469 
12470  if(ForwardEntryPtr->LocationName == "")
12471  {
12472  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
12473  ". One or other service does not have a location set");
12474  TrainDataVector.clear();
12475  Utilities->CallLogPop(526);
12476  return false;
12477  }
12478 
12479  if(ReverseEntryPtr->LocationName == "")
12480  {
12481  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
12482  ". One or other service does not have a location set");
12483  TrainDataVector.clear();
12484  Utilities->CallLogPop(527);
12485  return false;
12486  }
12487 
12488  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
12489  {
12490  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
12491  " is at a different location to the referencing train " + MainHeadCode);
12492  TrainDataVector.clear();
12493  Utilities->CallLogPop(842);
12494  return false;
12495  }
12496 
12497  // ignore shuttle repeat links for first time check
12498  if(!Shuttle)
12499  {
12500  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
12501  {
12502  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
12503  " has a different event time to the referencing train " + MainHeadCode);
12504  TrainDataVector.clear();
12505  Utilities->CallLogPop(525);
12506  return false;
12507  }
12508  }
12509 
12510  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
12511  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
12512  if(ForwardShuttleStart && ReverseShuttleFinish)
12513  // Shuttle must be true if these are true
12514  {
12515  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
12516  {
12517  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
12518  " first repeat restart time not consistent with finish service " + OtherHeadCode);
12519  TrainDataVector.clear();
12520  Utilities->CallLogPop(1055);
12521  return false;
12522  }
12523  }
12524 
12525  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
12526  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
12527  {
12528  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12529  {
12530  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
12531  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12532  TrainDataVector.clear();
12533  Utilities->CallLogPop(528);
12534  return false;
12535  }
12536  }
12537 
12538  if(ReverseEntryPtr->Command == "Fjo")
12539  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
12540  {
12541  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12542  {
12543  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
12544  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12545  TrainDataVector.clear();
12546  Utilities->CallLogPop(862);
12547  return false;
12548  }
12549  }
12550 
12551  if(ReverseEntryPtr->Command == "Fns")
12552  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
12553  {
12554  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12555  {
12556  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
12557  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12558  TrainDataVector.clear();
12559  Utilities->CallLogPop(529);
12560  return false;
12561  }
12562  }
12563 
12564  if(ForwardEntryPtr->Command == "Sfs")
12565  {
12566  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
12567  {
12568  SecondPassMessage(GiveMessages,
12569  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
12570  MainHeadCode);
12571  TrainDataVector.clear();
12572  Utilities->CallLogPop(530);
12573  return false;
12574  }
12575  }
12576 
12577  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
12578  {
12579  if(ReverseEntryPtr->Command != "Sfs")
12580  {
12581  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
12582  MainHeadCode);
12583  TrainDataVector.clear();
12584  Utilities->CallLogPop(839);
12585  return false;
12586  }
12587  else
12588  {
12589  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
12590  {
12591  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
12592  TrainDataVector.clear();
12593  Utilities->CallLogPop(849);
12594  return false;
12595  }
12596  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
12597  {
12598  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
12599  TrainDataVector.clear();
12600  Utilities->CallLogPop(850);
12601  return false;
12602  }
12603  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
12604  {
12605  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
12606  TrainDataVector.clear();
12607  Utilities->CallLogPop(846);
12608  return false;
12609  }
12610  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12611  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12612  if(OtherTrainDataPtr->Description == "")
12613  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
12614  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
12615  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12616  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12617  }
12618  }
12619 
12620  if(ForwardEntryPtr->Command == "Sns")
12621  {
12622  if(ReverseEntryPtr->Command != "Fns")
12623  {
12624  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
12625  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
12626  TrainDataVector.clear();
12627  Utilities->CallLogPop(531);
12628  return false;
12629  }
12630  }
12631 
12632  if(ForwardEntryPtr->Command == "Fns")
12633  {
12634  if(ReverseEntryPtr->Command != "Sns")
12635  {
12636  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
12637  " and forms a new service with headcode " + OtherHeadCode);
12638  TrainDataVector.clear();
12639  Utilities->CallLogPop(840);
12640  return false;
12641  }
12642  else
12643  {
12644  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12645  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12646  if(OtherTrainDataPtr->Description == "")
12647  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
12648  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12649  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12650  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12651  }
12652  }
12653  if(ForwardEntryPtr->Command == "jbo")
12654  {
12655  if(ReverseEntryPtr->Command != "Fjo")
12656  {
12657  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
12658  " and is joined by a train with headcode " + OtherHeadCode);
12659  TrainDataVector.clear();
12660  Utilities->CallLogPop(841);
12661  return false;
12662  }
12663  }
12664 
12665  if(ForwardEntryPtr->Command == "Fjo")
12666  {
12667  if(ReverseEntryPtr->Command != "jbo")
12668  {
12669  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
12670  " and joins a train with headcode " + OtherHeadCode);
12671  TrainDataVector.clear();
12672  Utilities->CallLogPop(532);
12673  return false;
12674  }
12675  else
12676  {
12677  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12678  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12679  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
12680  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12681  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
12682  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
12683  }
12684  }
12685 
12686  if(ForwardShuttleStart)
12687  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
12688  {
12689  if(!ReverseShuttleFinish)
12690  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
12691  {
12692  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
12693  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
12694  TrainDataVector.clear();
12695  Utilities->CallLogPop(1056);
12696  return false;
12697  }
12698  }
12699 
12700  if(ReverseShuttleStart)
12701  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
12702  {
12703  if(!ForwardShuttleFinish)
12704  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
12705  {
12706  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
12707  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
12708  TrainDataVector.clear();
12709  Utilities->CallLogPop(1057);
12710  return false;
12711  }
12712  else
12713  {
12714  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12715  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12716 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
12717  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
12718 */
12719  }
12720  }
12721 
12722  // check repeat information consistent if present
12723  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
12724  // and those not accessed here
12725 
12726  // still need to check the non-repeating links and that they have no repeats - do that outside this function
12727  bool MainRepeat = false, OtherRepeat = false;
12728  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
12729 
12730  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
12731  {
12732  MainRepeat = true;
12733  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
12734  }
12735  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
12736  {
12737  OtherRepeat = true;
12738  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
12739  }
12740  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
12741  {
12742  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
12743  " and the associated train with headcode " + OtherHeadCode);
12744  TrainDataVector.clear();
12745  Utilities->CallLogPop(844);
12746  return false;
12747  }
12748  if(MainRepeat && OtherRepeat)
12749  {
12750  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
12751  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
12752  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
12753  {
12754  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
12755  " and the associated train with headcode " + OtherHeadCode);
12756  TrainDataVector.clear();
12757  Utilities->CallLogPop(845);
12758  return false;
12759  }
12760  }
12761  Utilities->CallLogPop(863);
12762  return true;
12763 }
12764 
12765 // ---------------------------------------------------------------------------
12766 
12767 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
12768  // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
12769 {
12770  // strip spaces from extreme ends of input
12771  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
12772  if(Input == "")
12773  {
12774  Utilities->CallLogPop(856);
12775  return;
12776  }
12777  while(Input[1] == ' ')
12778  {
12779  if(Input.Length() > 1)
12780  {
12781  Input = Input.SubString(2, Input.Length() - 1);
12782  }
12783  else
12784  {
12785  Input = "";
12786  Utilities->CallLogPop(857);
12787  return;
12788  }
12789  }
12790  if(Input == "")
12791  {
12792  Utilities->CallLogPop(858);
12793  return;
12794  }
12795  while(Input[Input.Length()] == ' ')
12796  {
12797  if(Input.Length() > 1)
12798  {
12799  Input = Input.SubString(1, Input.Length() - 1);
12800  }
12801  else
12802  {
12803  Input = "";
12804  Utilities->CallLogPop(859);
12805  return;
12806  }
12807  }
12808  // now strip spaces immediately after all commas and semicolons within the text
12809  AnsiString Output = "";
12810  bool DelimiterFound = false;
12811 
12812  for(int x = 1; x < Input.Length() + 1; x++)
12813  {
12814  if(DelimiterFound)
12815  {
12816  if(Input[x] == ' ')
12817  continue;
12818  }
12819  if((Input[x] != ',') && (Input[x] != ';'))
12820  {
12821  DelimiterFound = false;
12822  Output = Output + Input[x];
12823  }
12824  else
12825  {
12826  DelimiterFound = true;
12827  Output = Output + Input[x];
12828  }
12829  }
12830  if(Output == "")
12831  {
12832  Input = "";
12833  Utilities->CallLogPop(860);
12834  return;
12835  }
12836  // now strip spaces immediately before all commas and semicolons within the text
12837  Input = Output;
12838  Output = "";
12839  DelimiterFound = false;
12840  for(int x = Input.Length(); x > 0; x--)
12841  {
12842  if(DelimiterFound)
12843  {
12844  if(Input[x] == ' ')
12845  continue;
12846  }
12847  if((Input[x] != ',') && (Input[x] != ';'))
12848  {
12849  DelimiterFound = false;
12850  Output = AnsiString(Input[x]) + Output;
12851  }
12852  else
12853  {
12854  DelimiterFound = true;
12855  Output = AnsiString(Input[x]) + Output;
12856  }
12857  }
12858  Input = Output;
12859  Utilities->CallLogPop(861);
12860 }
12861 
12862 // ---------------------------------------------------------------------------
12863 
12864 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
12865  // checks if an Snt or Snt-sh entry followed (somewhere, not necessarily immediately) by a TimeLoc has the same LocationName
12866  // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
12867  // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
12868  // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
12869  // are done in this function, they must be done elsewhere.
12870 {
12871  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
12872  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12873 
12874  LocationName = "";
12875  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
12876  {
12877  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
12878  }
12880  {
12881  Utilities->CallLogPop(852);
12882  return false;
12883  }
12884  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
12885  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
12886 
12887  if(LocRear != "")
12888  LocationName = LocRear;
12889  else
12890  LocationName = LocFront;
12891  if(LocationName == "")
12892  {
12893  Utilities->CallLogPop(1036);
12894  return false;
12895  }
12896  if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed == 0))
12897  {
12898  Utilities->CallLogPop(1773);
12899  return true;
12900  }
12901  else if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed > 0))
12902  {
12903  LocationName = "";
12904  Utilities->CallLogPop(1784);
12905  return false;
12906  }
12907 
12908  // here if not a signaller start entry so must be at least one more entry
12909  const TActionVectorEntry &AVEntry1 = TDEntry.ActionVector.at(1);
12910 
12911  // has to be at least 2 AV entries to pass the > 1 comma test in the preliminary check
12912  if(((AVEntry1.Command == "Frh") || (AVEntry1.Command == "Fjo") || (AVEntry1.Command == "F-nshs") || (AVEntry1.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
12913  { //added F-nshs at v2.5.1 so can stay at a
12914  Utilities->CallLogPop(1037); //location until become a new shuttle service
12915  return true; //added Fns at same time as saw no reason to exclude
12916  }
12917  AnsiString TimeLocLocationName;
12918  bool FoundFlag = false;
12919 
12920  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
12921  {
12922  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
12923  if(AVEntry.FormatType == TimeLoc)
12924  {
12925  FoundFlag = true;
12926  TimeLocLocationName = AVEntry.LocationName;
12927  break;
12928  }
12929  }
12930  if(!FoundFlag)
12931  {
12932  Utilities->CallLogPop(853);
12933  return false;
12934  }
12935  if(TimeLocLocationName == LocationName)
12936  {
12937  Utilities->CallLogPop(854);
12938  return true;
12939  }
12940  Utilities->CallLogPop(855);
12941  return false;
12942 }
12943 
12944 // ---------------------------------------------------------------------------
12945 
12946 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
12947 {
12948  // checks that the new train start elements are valid - both exist & are connected, and that not
12949  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
12950  // & not starting with front on a continuation
12951  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
12952  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
12953 
12954  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
12955  if(RearPosition < 0)
12956  // error message given in GetTrackVectorPositionFromString
12957  {
12958  Utilities->CallLogPop(759);
12959  return false;
12960  }
12961  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
12962  if(FrontPosition < 0)
12963  // error message given in GetTrackVectorPositionFromString
12964  {
12965  Utilities->CallLogPop(760);
12966  return false;
12967  }
12968  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
12969  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
12970  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
12971 
12972  // check front & rear connected
12973  for(int x = 0; x < 4; x++)
12974  {
12975  if(RearTrackElement.Conn[x] == FrontPosition)
12976  {
12977  RearExitPos = x;
12978  break;
12979  }
12980  if(x == 3)
12981  {
12982  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
12983  Utilities->CallLogPop(762);
12984  return false;
12985  }
12986  }
12987  // check not starting with front on a continuation
12988  if(FrontType == Continuation)
12989  {
12990  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
12991  Utilities->CallLogPop(937);
12992  return false;
12993  }
12994 
12995  // check not starting on a level crossing
12996  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
12997  {
12998  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
12999  Utilities->CallLogPop(1951);
13000  return false;
13001  }
13002  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
13003  {
13004  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
13005  Utilities->CallLogPop(1952);
13006  return false;
13007  }
13008 
13009  // check if trying to start on diverging leg of points
13010  if((RearType == Points) && (RearExitPos == 3))
13011  {
13012  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
13013  Utilities->CallLogPop(936);
13014  return false;
13015  }
13016 
13017  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
13018  {
13019  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
13020  Utilities->CallLogPop(1808);
13021  return false;
13022  }
13023  Utilities->CallLogPop(905);
13024  return true;
13025 }
13026 
13027 // ---------------------------------------------------------------------------
13028 
13029 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
13030  // Rear & front element validity already checked in CheckStartPositionValidity
13031  // This checks for points in correct orientation, no train at start position and not starting on a locked route
13032 {
13033  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
13034  AnsiString(RearExitPos));
13035  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
13036 
13037  if(RearTrackElement.TrackType == Continuation)
13038  EventType = FailTrainEntry;
13039  else
13040  EventType = FailCreateTrain;
13041  int FrontPosition = RearTrackElement.Conn[RearExitPos];
13042  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
13043  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
13044  TTrackType RearType = RearTrackElement.TrackType;
13045  TTrackType FrontType = FrontTrackElement.TrackType;
13046  AnsiString RearName, FrontName;
13047 
13048  if(RearTrackElement.ActiveTrackElementName != "")
13049  RearName = RearTrackElement.ActiveTrackElementName;
13050  else
13051  RearName = RearTrackElement.ElementID;
13052  if(FrontTrackElement.ActiveTrackElementName != "")
13053  FrontName = FrontTrackElement.ActiveTrackElementName;
13054  else
13055  FrontName = FrontTrackElement.ElementID;
13056 
13057  TPrefDirElement PrefDirElement; // needed for next function but not used
13058  int LockedVectorNumber; // needed for next function but not used
13059 
13060  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
13061  {
13062  if(ReportFlag)
13063  {
13064  if(EventType == FailCreateTrain)
13065  EventType = FailCreateLockedRoute;
13066  else
13067  EventType = FailEnterLockedRoute;
13068  LogActionError(47, HeadCode, "", EventType, FrontName);
13069  }
13070  Utilities->CallLogPop(940);
13071  return false;
13072  }
13073 
13074  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
13075  {
13076  if(ReportFlag)
13077  {
13078  if(EventType == FailCreateTrain)
13079  EventType = FailCreateLockedRoute;
13080  else
13081  EventType = FailEnterLockedRoute;
13082  LogActionError(48, HeadCode, "", EventType, RearName);
13083  }
13084  Utilities->CallLogPop(1809);
13085  return false;
13086  }
13087 
13088  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
13089  {
13090  if(ReportFlag)
13091  {
13092  LogActionError(27, HeadCode, "", EventType, RearName);
13093  }
13094  Utilities->CallLogPop(1810);
13095  return false;
13096  }
13097 
13098  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
13099  {
13100  if(ReportFlag)
13101  {
13102  if(EventType == FailCreateTrain)
13103  LogActionError(28, HeadCode, "", EventType, FrontName);
13104  else
13105  LogActionError(43, HeadCode, "", EventType, RearName);
13106  }
13107  Utilities->CallLogPop(941);
13108  return false;
13109  }
13110  if(RearType == Bridge)
13111  {
13112  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13113  {
13114  if(ReportFlag)
13115  {
13116  LogActionError(29, HeadCode, "", EventType, RearName);
13117  }
13118  Utilities->CallLogPop(942);
13119  return false;
13120  }
13121  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13122  {
13123  if(ReportFlag)
13124  {
13125  LogActionError(30, HeadCode, "", EventType, RearName);
13126  }
13127  Utilities->CallLogPop(943);
13128  return false;
13129  }
13130  }
13131  if(FrontType == Bridge)
13132  {
13133  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13134  {
13135  if(ReportFlag)
13136  {
13137  if(EventType == FailCreateTrain)
13138  LogActionError(31, HeadCode, "", EventType, FrontName);
13139  else
13140  LogActionError(44, HeadCode, "", EventType, RearName);
13141  }
13142  Utilities->CallLogPop(944);
13143  return false;
13144  }
13145  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13146  {
13147  if(ReportFlag)
13148  {
13149  if(EventType == FailCreateTrain)
13150  LogActionError(45, HeadCode, "", EventType, FrontName);
13151  else
13152  LogActionError(46, HeadCode, "", EventType, RearName);
13153  }
13154  Utilities->CallLogPop(945);
13155  return false;
13156  }
13157  }
13158 
13159  EventType = FailCreatePoints;
13160  if(RearType == Points)
13161  {
13162  if(RearTrackElement.Attribute == 1)
13163  {
13164  if(ReportFlag)
13165  {
13166  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
13167  }
13168  Utilities->CallLogPop(933);
13169  return false;
13170  }
13171  }
13172  if(FrontType == Points)
13173  {
13174  if(FrontTrackElement.Attribute == 1)
13175  {
13176  if(ReportFlag)
13177  {
13178  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
13179  }
13180  Utilities->CallLogPop(934);
13181  return false;
13182  }
13183  }
13184  Utilities->CallLogPop(939);
13185  return true;
13186 }
13187 
13188 // ---------------------------------------------------------------------------
13189 
13190 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
13191 {
13192  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
13193  "," + AnsiString(IncDigits));
13194  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
13195  {
13196  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
13197  }
13198  if(!Last2CharactersBothDigits(2, BaseHeadCode))
13199  {
13200  Utilities->CallLogPop(1893);
13201  return BaseHeadCode;
13202  }
13203  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
13204  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
13205 
13206  while(NextRepeatDigits >= 100)
13207  {
13208  NextRepeatDigits -= 100; // rolls over after 99
13209  }
13210  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
13211 
13212  if(NextRepeatDigitsStr.Length() < 2)
13213  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
13214  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
13215 
13216  Utilities->CallLogPop(1365);
13217  return NextRepeatHeadCode;
13218 }
13219 
13220 // ---------------------------------------------------------------------------
13221 
13222 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
13223 {
13224  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
13225  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
13226  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
13227 
13228  Utilities->CallLogPop(1366);
13229  return NextRepeatTime;
13230 }
13231 
13232 // ---------------------------------------------------------------------------
13233 
13234 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
13235  // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
13236 {
13237  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
13238  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
13239  int ForwardSecs = int(double(ForwardEventTime) * 86400);
13240  int ReverseSecs = int(double(ReverseEventTime) * 86400);
13241  int RepeatSecs = RepeatMinutes * 60;
13242 
13243  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
13244  {
13245  Utilities->CallLogPop(1367);
13246  return false;
13247  }
13248  else
13249  {
13250  Utilities->CallLogPop(1368);
13251  return true;
13252  }
13253 }
13254 
13255 // ---------------------------------------------------------------------------
13256 
13257 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
13258  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13259 {
13260  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
13261  NonRepeatingHeadCode);
13262  int ForwardCount = 0;
13263  int ReverseCount = 0;
13264  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13265  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13266  // Forward corresponds to Main, Reverse to Other
13267  TTrainDataEntry *MainTrainDataPtr = 0;
13268  TTrainDataEntry *OtherTrainDataPtr = 0;
13269 
13270  // forward check
13271  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13272  {
13273  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13274  if(TDEntry.HeadCode == MainHeadCode)
13275  {
13276  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13277  {
13278  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13279  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
13280  {
13281  MainTrainDataPtr = &TrainDataVector.at(x);
13282  ForwardEntryPtr = &AVEntry;
13283  ForwardCount++;
13284  ForwardTDVectorNumber = x;
13285  }
13286  }
13287  }
13288  }
13289  if(ForwardCount == 0)
13290  // this is an exception because the headcodes are selected in the same order as the forward check
13291  {
13292  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
13293  }
13294  if(ForwardCount > 1)
13295  {
13296  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
13297  MainHeadCode);
13298  TrainDataVector.clear();
13299  Utilities->CallLogPop(1061);
13300  return false;
13301  }
13302 
13303  // reverse check
13304  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13305  {
13306  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13307  if(TDEntry.HeadCode == NonRepeatingHeadCode)
13308  {
13309  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13310  {
13311  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13312  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13313  {
13314  OtherTrainDataPtr = &TrainDataVector.at(x);
13315  ReverseCount++;
13316  ReverseEntryPtr = &AVEntry;
13317  ReverseTDVectorNumber = x;
13318  }
13319  }
13320  }
13321  }
13322 
13323  if(ReverseCount == 0)
13324  {
13325  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
13326  TrainDataVector.clear();
13327  Utilities->CallLogPop(1062);
13328  return false;
13329  }
13330  if(ReverseCount > 1)
13331  {
13332  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13333  NonRepeatingHeadCode);
13334  TrainDataVector.clear();
13335  Utilities->CallLogPop(1063);
13336  return false;
13337  }
13338 
13339  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
13340  {
13341  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
13342  TrainDataVector.clear();
13343  Utilities->CallLogPop(1064);
13344  return false;
13345  }
13346 
13347  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
13348  {
13349  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
13350  TrainDataVector.clear();
13351  Utilities->CallLogPop(1065);
13352  return false;
13353  }
13354 
13355  if(ForwardEntryPtr->LocationName == "")
13356  {
13357  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
13358  ". One or other service does not have a location set");
13359  TrainDataVector.clear();
13360  Utilities->CallLogPop(1066);
13361  return false;
13362  }
13363 
13364  if(ReverseEntryPtr->LocationName == "")
13365  {
13366  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
13367  ". One or other service does not have a location set");
13368  TrainDataVector.clear();
13369  Utilities->CallLogPop(1067);
13370  return false;
13371  }
13372 
13373  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13374  {
13375  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
13376  " is at a different location to the referencing train " + MainHeadCode);
13377  TrainDataVector.clear();
13378  Utilities->CallLogPop(1068);
13379  return false;
13380  }
13381 
13382  if(ForwardEntryPtr->Command == "F-nshs")
13383  // i.e. the non repeating link into the shuttle service
13384  {
13385  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13386  {
13387  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
13388  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
13389  TrainDataVector.clear();
13390  Utilities->CallLogPop(1069);
13391  return false;
13392  }
13393  }
13394 
13395  if(ForwardEntryPtr->Command == "Fns-sh")
13396  // i.e. the non repeating link out from the shuttle service
13397  {
13398  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
13399  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
13400  {
13401  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
13402  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
13403  TrainDataVector.clear();
13404  Utilities->CallLogPop(1070);
13405  return false;
13406  }
13407  }
13408 
13409  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
13410  // i.e. a non repeating link to or from the shuttle service
13411  {
13412  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13413  {
13414  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
13415  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
13416  TrainDataVector.clear();
13417  Utilities->CallLogPop(1071);
13418  return false;
13419  }
13420  }
13421 /* it's allowed to have a different description
13422  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
13423  {
13424  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
13425  {
13426  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
13427  TrainDataVector.clear();
13428  Utilities->CallLogPop(1072);
13429  return false;
13430  }
13431  }
13432 */
13433  if(ForwardEntryPtr->Command == "Sns-sh")
13434  {
13435  if(ReverseEntryPtr->Command != "F-nshs")
13436  {
13437  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
13438  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
13439  TrainDataVector.clear();
13440  Utilities->CallLogPop(1073);
13441  return false;
13442  }
13443  }
13444 
13445  if(ForwardEntryPtr->Command == "F-nshs")
13446  {
13447  if(ReverseEntryPtr->Command != "Sns-sh")
13448  {
13449  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
13450  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
13451  TrainDataVector.clear();
13452  Utilities->CallLogPop(1074);
13453  return false;
13454  }
13455  else
13456  {
13457  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13458  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
13459  if(OtherTrainDataPtr->Description == "")
13460  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13461  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13462  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13463  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13464  }
13465  }
13466 
13467  if(ForwardEntryPtr->Command == "Sns-fsh")
13468  {
13469  if(ReverseEntryPtr->Command != "Fns-sh")
13470  {
13471  SecondPassMessage(GiveMessages,
13472  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
13473  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
13474  TrainDataVector.clear();
13475  Utilities->CallLogPop(1075);
13476  return false;
13477  }
13478  }
13479 
13480  if(ForwardEntryPtr->Command == "Fns-sh")
13481  {
13482  if(ReverseEntryPtr->Command != "Sns-fsh")
13483  {
13484  SecondPassMessage(GiveMessages,
13485  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
13486  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
13487  TrainDataVector.clear();
13488  Utilities->CallLogPop(1076);
13489  return false;
13490  }
13491  else
13492  {
13493  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
13494  // links to the non-repeating non-shuttle linked service
13495  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13496  // needed for creating formatted timetable
13497  if(OtherTrainDataPtr->Description == "")
13498  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13499  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13500  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13501  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13502  }
13503  }
13504 
13505  Utilities->CallLogPop(1077);
13506  return true;
13507 }
13508 
13509 // ---------------------------------------------------------------------------
13510 
13511 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
13512  // Forward train is the finish shuttle entry 'Fns-sh'.
13513  // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
13514 {
13515  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
13516  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
13517  int ForwardSecs = int(double(ForwardEventTime) * 86400);
13518  int ReverseSecs = int(double(ReverseEventTime) * 86400);
13519  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
13520 
13521  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
13522  {
13523  Utilities->CallLogPop(1369);
13524  return false;
13525  }
13526  else
13527  {
13528  Utilities->CallLogPop(1370);
13529  return true;
13530  }
13531 }
13532 
13533 // ---------------------------------------------------------------------------
13534 
13535 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
13536  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13537  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
13538  // don't ever need to and as designed would skip repeats.
13539 
13540  // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
13541 {
13542  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
13543  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
13544  {
13545  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
13546  }
13547  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
13548  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
13549  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
13550 
13551  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
13552  {
13553  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
13554  TrainDataVector.clear();
13555  Utilities->CallLogPop(1091);
13556  return false;
13557  }
13558  while(LastActionCommand == "Fns")
13559  {
13560  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
13561  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
13562  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
13563  {
13564  SecondPassMessage(GiveMessages,
13565  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
13566  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
13567  TrainDataVector.clear();
13568  Utilities->CallLogPop(1092);
13569  return false;
13570  }
13571  }
13572  // exit the 'while' with LastActionCommand FSH-XX
13573  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
13574  {
13575  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
13576  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
13577  ". The linking of two or more shuttles is not permitted.");
13578  TrainDataVector.clear();
13579  Utilities->CallLogPop(1093);
13580  return false;
13581  }
13582  Utilities->CallLogPop(1094);
13583  return true;
13584 }
13585 
13586 // ---------------------------------------------------------------------------
13587 
13588 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
13589 {
13590  if(!GiveMessages)
13591  return;
13592 
13593  // if(ServiceReference == "") ShowMessage(Message);
13594  if(!CheckHeadCodeValidity(12, false, ServiceReference))
13595  ShowMessage(Message);
13596  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
13597  // false means don't give messages within the function
13598  else
13599  ShowMessage("Service " + ServiceReference + ": " + Message);
13600 }
13601 
13602 // ---------------------------------------------------------------------------
13603 
13604 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
13605 {
13606  if(!GiveMessages)
13607  return;
13608 
13609  ShowMessage(Message);
13610 }
13611 
13612 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
13613 // ---------------------------------------------------------------------------
13614 
13615 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
13616  // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
13617  // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
13618  // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
13619  // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
13620  // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
13621  // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
13622  // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
13623  // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
13624  // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
13625  // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
13626  // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
13627  // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
13628  // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
13629  // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
13630  // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
13631  // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
13632  // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
13633  // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
13634  // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
13635  // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
13636  // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
13637  // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
13638  // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
13639  // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
13640  // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
13641  // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
13642  // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
13643  // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
13644  // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
13645 {
13646  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
13647  AnsiString(ActionEventType) + "," + LocationID);
13648  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
13649 
13650  Prefix = " ERROR: ";
13651  if(ActionEventType == FailTrainEntry)
13652  {
13653  Prefix = " HELD: ";
13654  ErrorLog = " can't enter railway, train obstructing entry position ";
13655  WarningStr = " can't enter railway, train obstructing entry position ";
13656  Display->WarningLog(1, HeadCode + WarningStr + LocationID);
13657  }
13658  else if(ActionEventType == FailCreateTrain)
13659  {
13660  Prefix = " HELD: ";
13661  ErrorLog = " can't be created, train obstructing ";
13662  WarningStr = " can't be created, train obstructing ";
13663  Display->WarningLog(2, HeadCode + WarningStr + LocationID);
13664  }
13665  else if(ActionEventType == FailCreateLockedRoute)
13666  {
13667  Prefix = " HELD: ";
13668  ErrorLog = " can't be created on a locked route at ";
13669  WarningStr = " can't be created on a locked route at ";
13670  Display->WarningLog(4, HeadCode + WarningStr + LocationID);
13671  }
13672  else if(ActionEventType == FailEnterLockedRoute)
13673  {
13674  Prefix = " HELD: ";
13675  ErrorLog = " can't enter on a locked route at ";
13676  WarningStr = " can't enter on a locked route at ";
13677  Display->WarningLog(5, HeadCode + WarningStr + LocationID);
13678  }
13679  else if(ActionEventType == FailCreatePoints)
13680  {
13681  Prefix = " HELD: ";
13682  ErrorLog = " can't be created, diverging points at ";
13683  WarningStr = " can't be created, diverging points at ";
13684  Display->WarningLog(3, HeadCode + WarningStr + LocationID);
13685  }
13686  else if(ActionEventType == FailUnexpectedExitRailway)
13687  {
13688  ErrorLog = " left railway unexpectedly at ";
13689  UnexpectedExits++;
13690  }
13691  else if(ActionEventType == FailIncorrectExit)
13692  {
13693  ErrorLog = " left railway at an incorrect exit at ";
13694  IncorrectExits++;
13695  }
13696  else if(ActionEventType == FailLocTooShort)
13697  {
13698  ErrorLog = " failed to split - location too short at ";
13699  WarningStr = " failed to split, location too short at ";
13700  Display->WarningLog(6, HeadCode + WarningStr + LocationID);
13701  }
13702  else if(ActionEventType == FailSplitDueToOtherTrain)
13703  {
13704  Prefix = " HELD: ";
13705  ErrorLog = " unable to split - other train obstructing at ";
13706  WarningStr = " unable to split - other train obstructing at ";
13707  Display->WarningLog(7, HeadCode + WarningStr + LocationID);
13708  }
13709  else if(ActionEventType == FailUnexpectedBuffers)
13710  ErrorLog = " stopped at buffers unexpectedly at position ";
13711  else if(ActionEventType == FailMissedArrival)
13712  {
13713  ErrorLog = " failed to stop at ";
13714  MissedStops++;
13715  }
13716  else if(ActionEventType == FailMissedSplit)
13717  {
13718  ErrorLog = " failed to split at ";
13720  }
13721  else if(ActionEventType == FailMissedJBO)
13722  {
13723  ErrorLog = " failed to be joined by other train at ";
13725  }
13726  else if(ActionEventType == FailMissedJoinOther)
13727  {
13728  ErrorLog = " failed to join other train at ";
13730  }
13731  else if(ActionEventType == FailMissedTerminate)
13732  {
13733  ErrorLog = " failed to terminate at ";
13735  }
13736  else if(ActionEventType == FailMissedNewService)
13737  {
13738  ErrorLog = " failed to form new service at ";
13740  }
13741  else if(ActionEventType == FailMissedExitRailway)
13742  {
13743  ErrorLog = " failed to exit railway ";
13745  }
13746  else if(ActionEventType == FailMissedChangeDirection)
13747  {
13748  ErrorLog = " failed to change direction at ";
13750  }
13751  else if(ActionEventType == FailMissedPass)
13752  {
13753  ErrorLog = " failed to pass ";
13755  }
13756  else if(ActionEventType == FailBuffersPreventingStart)
13757  ErrorLog = " facing buffers and unable to start at ";
13758  else if(ActionEventType == FailDerailed)
13759  {
13760  ErrorLog = " DERAILED at position ";
13761  Prefix = " DERAILMENT: ";
13762  Derailments++;
13763  }
13764  else if(ActionEventType == FailBufferCrash)
13765  {
13766  ErrorLog = " CRASHED INTO BUFFERS at ";
13767  Prefix = " CRASH: ";
13768  CrashedTrains++;
13769  }
13770  else if(ActionEventType == FailLevelCrossingCrash)
13771  {
13772  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
13773  Prefix = " CRASH: ";
13774  CrashedTrains++;
13775  }
13776  else if(ActionEventType == FailCrashed)
13777  {
13778  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
13779  Prefix = " CRASH: ";
13780  CrashedTrains++;
13781  CrashedTrains++;
13782  }
13783  else if(ActionEventType == FailSPAD)
13784  {
13785  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
13786  Prefix = " SPAD: ";
13787  SPADEvents++;
13788  }
13789  else if(ActionEventType == FailLockedRoute)
13790  {
13791  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
13792  Prefix = " SPAD RISK: ";
13793  SPADRisks++;
13794  }
13795  else if(ActionEventType == RouteForceCancelled)
13796  {
13797  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
13798  }
13799  else if(ActionEventType == WaitingForJBO)
13800  {
13801  Prefix = " WARNING: ";
13802  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
13803  WarningStr = " waiting to join " + OtherHeadCode + " at ";
13804  Display->WarningLog(8, HeadCode + WarningStr + LocationID);
13805  }
13806  else if(ActionEventType == WaitingForFJO)
13807  {
13808  Prefix = " WARNING: ";
13809  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
13810  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
13811  Display->WarningLog(9, HeadCode + WarningStr + LocationID);
13812  }
13813  TDateTime ActualTime = TrainController->TTClockTime;
13814 
13815  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
13816  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
13817  Utilities->CallLogPop(1371);
13818 }
13819 
13820 // ---------------------------------------------------------------------------
13821 
13823 {
13824 /*
13825  TrainDataEntry
13826  AnsiString HeadCode, Description;//null on creation
13827  int StartSpeed, MaxRunningSpeed;//both kph
13828  int RepeatNumber;
13829  TActionVector ActionVector;
13830  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
13831  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
13832 
13833  ActionVectorEntry
13834  TTimetableEntryType FormatType;
13835  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
13836  AnsiString LocationName, Command, OtherHeadCode;//null on creation
13837  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
13838  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
13839  int RepeatNumber;
13840 
13841  TrainOperatingData
13842  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
13843  int TrainID;
13844  TRunningEntry RunningEntry;
13845  TDateTime StartTime;
13846  AnsiString HeadCode;
13847 */
13848  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
13849  std::ofstream OutFile("TrainData.csv");
13850 
13851  if(OutFile == 0)
13852  {
13853  ShowMessage("Output file TrainData.csv failed to open");
13854  Utilities->CallLogPop(1372);
13855  return;
13856  }
13857  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13858  {
13859  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13860  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
13861 
13862  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
13863  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
13864 
13865  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
13866  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
13867  "RepeatNumber" << '\n' << '\n';
13868  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13869  {
13870  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13871  AnsiString TimetableEntryTypeStr;
13872  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
13873  switch(AVEntry.FormatType)
13874  {
13875  case 0:
13876  {
13877  TimetableEntryTypeStr = "NoFormat";
13878  break;
13879  }
13880 
13881  case 1:
13882  {
13883  TimetableEntryTypeStr = "TimeLoc";
13884  break;
13885  }
13886 
13887  case 2:
13888  {
13889  TimetableEntryTypeStr = "TimeTimeLoc";
13890  break;
13891  }
13892 
13893  case 3:
13894  {
13895  TimetableEntryTypeStr = "TimeCmd";
13896  break;
13897  }
13898 
13899  case 4:
13900  {
13901  TimetableEntryTypeStr = "StartNew";
13902  break;
13903  }
13904 
13905  case 5:
13906  {
13907  TimetableEntryTypeStr = "TimeCmdHeadCode";
13908  break;
13909  }
13910 
13911  case 6:
13912  {
13913  TimetableEntryTypeStr = "FinRemHere";
13914  break;
13915  }
13916 
13917  case 7:
13918  {
13919  TimetableEntryTypeStr = "FNSShuttle";
13920  break;
13921  }
13922 
13923  case 8:
13924  {
13925  TimetableEntryTypeStr = "SNTShuttle";
13926  break;
13927  }
13928 
13929  case 9:
13930  {
13931  TimetableEntryTypeStr = "SNSShuttle";
13932  break;
13933  }
13934 
13935  case 10:
13936  {
13937  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
13938  break;
13939  }
13940 
13941  case 11:
13942  {
13943  TimetableEntryTypeStr = "FSHNewService";
13944  break;
13945  }
13946 
13947  case 12:
13948  {
13949  TimetableEntryTypeStr = "Repeat";
13950  break;
13951  }
13952 
13953  default:
13954  {
13955  TimetableEntryTypeStr = "Default";
13956  break;
13957  }}
13958  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
13959  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
13960  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
13961  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
13962  AVEntry.NumberOfRepeats << '\n';
13963  }
13964  OutFile << '\n';
13965  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
13966  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
13967  {
13968  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
13969  AnsiString RunningEntryStr;
13970  // NotStarted, Running, Exited
13971  switch(TOD.RunningEntry)
13972  {
13973  case 0:
13974  {
13975  RunningEntryStr = "NotStarted";
13976  break;
13977  }
13978 
13979  case 1:
13980  {
13981  RunningEntryStr = "Running";
13982  break;
13983  }
13984 
13985  case 2:
13986  {
13987  RunningEntryStr = "Exited";
13988  break;
13989  }}
13990  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
13991  }
13992  OutFile << '\n';
13993  }
13994  OutFile.close();
13995  Utilities->CallLogPop(1373);
13996 }
13997 
13998 // ---------------------------------------------------------------------------
13999 
14000 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
14001  // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
14002  // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
14003 {
14004  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
14005  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14007  ShowMessage(Message);
14008  BaseTime = TDateTime::CurrentDateTime();
14009  StopTTClockFlag = false;
14010  Utilities->CallLogPop(1374);
14011 }
14012 
14013 // ---------------------------------------------------------------------------
14014 
14015 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
14016  // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
14017  // from the start of the relevant vectors. Can't save the pointer values
14018  // as these will be different each time the vectors are created
14019 {
14020  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
14021  Utilities->SaveFileInt(SessionFile, TrainVector.size());
14022  for(unsigned int x = 0; x < TrainVector.size(); x++)
14023  {
14024  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
14025  }
14026  Utilities->CallLogPop(1375);
14027 }
14028 
14029 // ---------------------------------------------------------------------------
14030 
14031 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
14032 {
14033  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
14034  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
14035  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14036 
14037  // by zero error in calculating AValue, use 1
14038  for(int x = 0; x < NumberOfTrains; x++)
14039  {
14040  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14041  // by zero error in calculating AValue, use 1
14042  NewTrain->LoadOneSessionTrain(0, SessionFile);
14043  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
14044  // added at v2.4.0. have to include as that value not stored in session file
14045  {
14046  NewTrain->StoppedWithoutPower = true;
14047  }
14048  TrainVector.push_back(*NewTrain);
14049  LastTrainLoaded = x;
14050  }
14051  delete NewTrain;
14052  Utilities->CallLogPop(1376);
14053 }
14054 
14055 // ---------------------------------------------------------------------------
14056 
14057 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
14058 {
14059  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
14060  int NumberOfTrains;
14061 
14062  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
14063  {
14064  Utilities->CallLogPop(1377);
14065  return false;
14066  }
14067  for(int x = 0; x < NumberOfTrains; x++)
14068  {
14069  if(!(TTrain::CheckOneSessionTrain(InFile)))
14070  {
14071  Utilities->CallLogPop(1378);
14072  return false;
14073  }
14074  }
14075  Utilities->CallLogPop(1379);
14076  return true;
14077 }
14078 
14079 // ---------------------------------------------------------------------------
14080 
14081 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
14082 {
14083  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
14084  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
14085  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
14086  {
14087  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
14088  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
14089  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
14090  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
14091  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
14092  }
14093  Utilities->CallLogPop(1380);
14094 }
14095 
14096 // ---------------------------------------------------------------------------
14097 
14098 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14099 {
14100  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
14101  TAllRoutes::TLockedRouteClass LockedRouteObject;
14102  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
14103 
14104  for(int x = 0; x < LockedRouteVectorSize; x++)
14105  {
14106  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14107  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14108  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14109  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
14110  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
14111  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
14112  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
14113  }
14114  Utilities->CallLogPop(1381);
14115 }
14116 
14117 // ---------------------------------------------------------------------------
14118 
14119 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14120 {
14121  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
14122  int LockedRouteVectorSize;
14123 
14124  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
14125  {
14126  Utilities->CallLogPop(1382);
14127  return false;
14128  }
14129  for(int x = 0; x < LockedRouteVectorSize; x++)
14130  {
14131  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14132  {
14133  Utilities->CallLogPop(1383);
14134  return false;
14135  }
14136  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14137  {
14138  Utilities->CallLogPop(1384);
14139  return false;
14140  }
14141  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14142  {
14143  Utilities->CallLogPop(1385);
14144  return false;
14145  }
14146  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
14147  {
14148  Utilities->CallLogPop(1386);
14149  return false;
14150  }
14151  if(!Utilities->CheckFileDouble(SessionFile))
14152  {
14153  Utilities->CallLogPop(1387);
14154  return false;
14155  }
14156  }
14157  Utilities->CallLogPop(1388);
14158  return true;
14159 }
14160 
14161 // ---------------------------------------------------------------------------
14162 
14163 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
14164 {
14165  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
14166  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
14167  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
14168  {
14169  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
14170  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
14171  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
14172  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
14173  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
14174  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
14175  }
14176  Utilities->CallLogPop(1389);
14177 }
14178 
14179 // ---------------------------------------------------------------------------
14180 
14181 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
14182 {
14183  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
14184  TContinuationAutoSigEntry ContinuationAutoSigObject;
14185  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
14186 
14187  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
14188  {
14189  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14190  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
14191  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
14192  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
14193  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
14194  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
14195  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
14196  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
14197  }
14198  Utilities->CallLogPop(1390);
14199 }
14200 
14201 // ---------------------------------------------------------------------------
14202 
14203 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
14204 {
14205  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
14206  int ContinuationAutoSigVectorSize;
14207 
14208  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
14209  {
14210  Utilities->CallLogPop(1391);
14211  return false;
14212  }
14213  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
14214  {
14215  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14216  {
14217  Utilities->CallLogPop(1392);
14218  return false;
14219  }
14220  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
14221  {
14222  Utilities->CallLogPop(1393);
14223  return false;
14224  }
14225  if(!Utilities->CheckFileDouble(SessionFile))
14226  {
14227  Utilities->CallLogPop(1405);
14228  return false;
14229  }
14230  if(!Utilities->CheckFileDouble(SessionFile))
14231  {
14232  Utilities->CallLogPop(1406);
14233  return false;
14234  }
14235  if(!Utilities->CheckFileDouble(SessionFile))
14236  {
14237  Utilities->CallLogPop(1407);
14238  return false;
14239  }
14240  if(!Utilities->CheckFileDouble(SessionFile))
14241  {
14242  Utilities->CallLogPop(1394);
14243  return false;
14244  }
14245  }
14246  Utilities->CallLogPop(1395);
14247  return true;
14248 }
14249 
14250 // ---------------------------------------------------------------------------
14251 
14252 /*
14253  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
14254  {
14255  public:
14256  AnsiString Description; ///< service description
14257  AnsiString HeadCode; ///< service headcode
14258  int RepeatNumber; ///< service RepeatNumber
14259  int IncrementalMinutes; ///< Repeat separation in minutes
14260  int IncrementalDigits; ///< Repeat headcode separation
14261  int VectorPosition; ///< TrackVectorPosition for the continuation element
14262  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
14263  };
14264 
14265 
14266  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
14267  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
14268 */
14269 
14271  // build this into timetable load so session loading can use it too
14272  // being a multimap it automatically sorts in ascending EventTime order
14273 {
14274  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
14276  // need to clear as this called twice when load a session
14277  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14278  {
14279  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14280  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
14281  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
14282 
14283  if(AVFirstEntry.Command == "Snt")
14284  // new train (no need to include Snt-sh since they can't start at a continuation)
14285  {
14288  {
14290  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
14291  // retains this value for all repeats
14292  CTEEntry.RepeatNumber = 0; // for first entry
14293  CTEEntry.TrainDataEntryPtr = &TDEntry;
14294  // retains this value for all repeats
14295  CTEEntry.HeadCode = TDEntry.HeadCode;
14296  CTEEntry.Description = TDEntry.Description;
14297  CTEEntry.IncrementalMinutes = 0;
14298  CTEEntry.IncrementalDigits = 0;
14299  if(AVLastEntry.FormatType == Repeat)
14300  {
14301  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
14302  // retains this value or 0 for all repeats
14303  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
14304  // retains this value or 0 for all repeats
14305  }
14306  CTEMMP.first = AVFirstEntry.EventTime;
14307  CTEMMP.second = CTEEntry;
14308  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
14309  // base entry
14310  if(TDEntry.NumberOfTrains > 1)
14311  {
14312  if(AVLastEntry.FormatType != Repeat)
14313  {
14314  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
14315  }
14316  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
14317  {
14318  CTEEntry.RepeatNumber = y;
14319  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
14320  // CTEEntry.VectorPosition stays same
14321  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
14322  CTEMMP.second = CTEEntry;
14323  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
14324  }
14325  }
14326  }
14327  }
14328  }
14329  Utilities->CallLogPop(1396);
14330 }
14331 
14332 // ---------------------------------------------------------------------------
14333 
14335 {
14336  // called when WarningFlashCount == 0 or when press zoomout button
14337  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
14338  if(!Display->ZoomOutFlag)
14339  {
14340  Utilities->CallLogPop(1156);
14341  return;
14342  }
14343  for(unsigned int x = 0; x < TrainVector.size(); x++)
14344  {
14345  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
14346  // if OldPlotElement[x] == -1 then ignore (not plotted)
14348  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
14349  }
14350  Display->Update();
14351  // need to keep this since Update() not called for PlotSmallOutput as too slow
14352  Utilities->CallLogPop(742);
14353 }
14354 
14355 // ---------------------------------------------------------------------------
14356 
14357 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
14358 {
14359  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
14360  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
14361  {
14362  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
14363  }
14364  Utilities->CallLogPop(740);
14365  return TrainVector.at(VecPos);
14366 }
14367 
14368 // ---------------------------------------------------------------------------
14369 
14370 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
14371 {
14372  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
14373  AnsiString RetStr = "", PartStr = "";
14374 
14375 
14376 /*
14377  Have description & mass etc for train at top - header, then array of actions
14378 
14379  class TActionVectorEntry
14380  {
14381  public:
14382  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
14384  bool SignallerControl;
14386  bool Warning;
14388  int NumberOfRepeats;
14390  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
14392  TDateTime EventTime, ArrivalTime, DepartureTime;
14394  TExitList ExitList;
14396  TTimetableFormatType FormatType;
14398  TTimetableLocationType LocationType;
14400  TTimetableSequenceType SequenceType;
14402  TTimetableShuttleLinkType ShuttleLinkType;
14404  TTrainDataEntry *LinkedTrainEntryPtr;
14406  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
14408 
14409  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
14410 
14411  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
14412 
14413  class TTrainOperatingData
14414  {
14415  public:
14416  int TrainID;
14417  TActionEventType EventReported;
14418  TRunningEntry RunningEntry;
14419 
14420  //inline function
14421  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
14422  };
14423 
14424  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
14425 
14426  class TTrainDataEntry
14427  {
14428  public:
14429  AnsiString HeadCode, ServiceReference, Description;
14431  double MaxBrakeRate;
14433  double MaxRunningSpeed;
14435  double PowerAtRail;
14437  int Mass;
14439  int NumberOfTrains;
14441  int SignallerSpeed;
14443  int StartSpeed;
14445  TActionVector ActionVector;
14447  TTrainOperatingDataVector TrainOperatingDataVector;
14449 
14450  //inline function
14451  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
14452  };
14453 
14454  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
14455 
14456  //formatted timetable types
14457  class TOneTrainFormattedEntry
14458  {
14459  AnsiString Action;//includes location if relevanr
14460  AnsiString Time;
14461  };
14462 
14463  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
14464 
14465  class TOneCompleteFormattedTrain//headcode + list of actions
14466  {
14467  public:
14468  AnsiString HeadCode;
14469  TOneFormattedTrainVector OneFormattedTrainVector;
14470  };
14471 
14472  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
14473 
14474  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
14475  {
14476  public:
14477  AnsiString Header;//description, mass, power, brake rate etc
14478  int NumberOfTrains;// number of repeats + 1
14479  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
14480  };
14481 
14482 
14483  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
14484  //end of formatted timetable types
14485 
14486 */
14487 
14488  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
14489 
14490  // format "16/06/2009 20:55:17"
14491  // avoid characters in filename:= / \ : * ? " < > |
14492  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
14493 
14494  AnsiString ShortTTName = "";
14495 
14496  for(int x = TTFileName.Length(); x > 0; x--)
14497  {
14498  if(TTFileName[x] == '\\')
14499  {
14500  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
14501  break;
14502  }
14503  }
14504 
14505  ShowMessage("Creates two timetables named " + ShortTTName +
14506  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
14507 
14508  Screen->Cursor = TCursor(-11); // Hourglass
14509 
14510  AnsiString FormatNoDPStr = "#######0";
14511  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
14512 
14514  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
14515  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
14516 
14517  // all timetable in formatted form
14518  //create the AllTTTrains vector
14519  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14520  {
14521  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
14522  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
14523  if(TrainDataEntry.Mass > 0)
14524  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
14525  if(TrainDataEntry.PowerAtRail > 0)
14526  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
14527  if(TrainDataEntry.MaxBrakeRate > 0)
14528  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
14529  if(TrainDataEntry.MaxRunningSpeed > 0)
14530  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
14531  FirstHeadCode = TrainDataEntry.HeadCode;
14532  int IncDigits = 0, IncMinutes = 0;
14533  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
14534  if(!ActionVector.empty())
14535  {
14536  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
14537  {
14538  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
14539  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
14540  }
14541  }
14542 
14543  TTrainFormattedInformation OneTTLine;
14544  // contains all information for a single TT entry (including repeats)
14545  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
14546  {
14547  OneTTLine.Header = "";
14548  if((TrainDataEntry.Description != "") && (MassStr != ""))
14549  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
14550  else if(TrainDataEntry.Description != "")
14551  OneTTLine.Header = TrainDataEntry.Description;
14552  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
14553  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
14554  for(unsigned int z = 0; z < ActionVector.size(); z++)
14555  {
14556  TOneTrainFormattedEntry OneTTEntry;
14557  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
14558  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
14559  AnsiString PartStr = "", TimeStr = "";
14560 /*
14561  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
14562  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
14563  ExitRailway};
14564  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
14565  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
14566  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
14567 */
14568  if(ActionVectorEntry.SequenceType == Start)
14569  {
14570  if(ActionVectorEntry.FormatType == StartNew)
14571  {
14572  if(ActionVectorEntry.LocationName != "")
14573  {
14574  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
14575  {
14576  PartStr = "Enters at " + ActionVectorEntry.LocationName;
14577  }
14578  else
14579  {
14580  PartStr = "Created at " + ActionVectorEntry.LocationName;
14581  }
14582  }
14583  else // may be a named continuation or other element, and if so report that
14584  {
14585  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
14586  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
14587  {
14588  if(LocName != "")
14589  {
14590  PartStr = "Enters at " + LocName;
14591  }
14592  else // use rear position if it's a continuation
14593  {
14594  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
14595  }
14596  }
14597  else // not a continuation
14598  {
14599  if(LocName != "")
14600  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
14601  // but include anyway
14602  {
14603  PartStr = "Created at " + LocName;
14604  }
14605  else // use rear position again
14606  {
14607  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
14608  }
14609  }
14610  }
14611  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
14612  }
14613  else if(ActionVectorEntry.FormatType == SNTShuttle)
14614  {
14615  if(y == 0) // first train
14616  {
14617  PartStr = "Enters at " + ActionVectorEntry.LocationName;
14618  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
14619  }
14620  else
14621  {
14622  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
14623  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
14624  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
14625  } // y-1 for headcode above since it is the last repeat value that the train is from
14626  }
14627  else if(ActionVectorEntry.Command == "Sfs")
14628  {
14629  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
14630  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14631  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
14632  }
14633  else if(ActionVectorEntry.Command == "Sns")
14634  {
14635  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14636  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14637  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
14638  }
14639  else if(ActionVectorEntry.FormatType == SNSShuttle)
14640  {
14641  if(y == 0) // first entry from shuttle
14642  {
14643  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14644  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
14645  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
14646  }
14647  else
14648  {
14649  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
14650  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
14651  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
14652  } // y-1 for headcode above since it is the last repeat value that the train is from
14653  }
14654  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
14655  {
14656  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14657  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
14658  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
14659  AnsiString FirstHeadCode = TDE->HeadCode;
14660  int LastRepeatNumber = TDE->NumberOfTrains - 1;
14661  // a shuttle has to have at least 1 repeat
14662  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
14663  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
14664  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
14665  }
14666  }
14667  else if(ActionVectorEntry.SequenceType == Intermediate)
14668  {
14669  if(ActionVectorEntry.FormatType == TimeTimeLoc)
14670  {
14671  // here need 2 entries if times different so push the first right away & the second later
14672  // if times same just give the arrival entry
14673  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
14674  {
14675  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
14676  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14677  OneTTEntry.Action = PartStr;
14678  OneTTEntry.Time = TimeStr;
14679  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
14680  PartStr = "Departs from " + ActionVectorEntry.LocationName;
14681  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
14682  }
14683  else
14684  {
14685  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
14686  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14687  }
14688  }
14689  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
14690  {
14691  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
14692  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14693  }
14694  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
14695  {
14696  PartStr = "Departs from " + ActionVectorEntry.LocationName;
14697  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
14698  }
14699  else if(ActionVectorEntry.FormatType == PassTime)
14700  {
14701  PartStr = "Passes " + ActionVectorEntry.LocationName;
14702  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
14703  }
14704  else if(ActionVectorEntry.Command == "jbo")
14705  {
14706  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
14707  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14708  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
14709  }
14710  else if(ActionVectorEntry.Command == "fsp")
14711  {
14712  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
14713  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14714  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
14715  }
14716  else if(ActionVectorEntry.Command == "rsp")
14717  {
14718  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
14719  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14720  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
14721  }
14722  else if(ActionVectorEntry.Command == "cdt")
14723  {
14724  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
14725  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
14726  }
14727  }
14728  else if(ActionVectorEntry.SequenceType == Finish)
14729  {
14730  if(ActionVectorEntry.Command == "Fns")
14731  {
14732  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14733  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14734  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
14735  }
14736  else if(ActionVectorEntry.Command == "F-nshs")
14737  {
14738  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14739  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
14740  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
14741  }
14742  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
14743  {
14744  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
14745  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
14746  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
14747  // y+1 because it's the NEXT service repeat number that is relevant
14748  }
14749  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
14750  {
14751  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14752  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
14753  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
14754  }
14755  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
14756  {
14757  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14758  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
14759  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
14760  // y+1 because it's the NEXT service repeat number that is relevant
14761  }
14762  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
14763  {
14764  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
14765  // only used in chronological tt
14766  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
14767  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
14768  }
14769  else if(ActionVectorEntry.Command == "Frh")
14770  {
14771  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
14772  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
14773  if(z > 0)
14774  // should be for finish entry but include check for safety
14775  {
14776  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
14777  {
14778  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
14779  }
14780  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
14781  {
14782  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
14783  }
14784  else
14785  TimeStr = " "; // shouldn't ever get here
14786  }
14787  }
14788  else if(ActionVectorEntry.Command == "Fer")
14789  {
14790  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList);
14791  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
14792  }
14793  else if(ActionVectorEntry.Command == "Fjo")
14794  {
14795  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
14796  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14797  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
14798  }
14799  }
14800  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
14801  continue; // no entry needed for a repeat
14802  OneTTEntry.Action = PartStr;
14803  OneTTEntry.Time = TimeStr;
14804  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
14805  // one per action
14806  }
14807  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
14808  // one per repeat
14809  }
14810  AllTTTrains->push_back(OneTTLine); // one per repeating train
14811  }
14812  // AllTTTrains vector now complete
14813 
14814  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
14815 
14816  if(TTFile == 0)
14817  {
14818  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
14819  delete AllTTTrains;
14820  Utilities->CallLogPop(1567);
14821  return;
14822  }
14823 
14824 /* formatted timetable types
14825  class TOneTrainFormattedEntry
14826  {
14827  AnsiString Action;//includes location if relevant
14828  AnsiString Time;
14829  };
14830 
14831  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
14832 
14833  class TOneCompleteFormattedTrain//headcode + list of actions
14834  {
14835  public:
14836  AnsiString HeadCode;
14837  TOneFormattedTrainVector OneFormattedTrainVector;
14838  };
14839 
14840  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
14841 
14842  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
14843  {
14844  public:
14845  AnsiString Header;//description, mass, power, brake rate etc
14846  int NumberOfTrains;// number of repeats + 1
14847  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
14848  };
14849 
14850  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
14851  //end of formatted timetable types
14852 */
14853 
14854  // new layout using multiple rows
14855  TTFile << TableTitle.c_str() << '\n' << '\n';
14856  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
14857  {
14858  TTFile << AllTTTrains->at(x).Header.c_str();
14859  TTFile << '\n';
14860  TTFile << ','; // for the blank line above the action list
14861  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14862  {
14863  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
14864  {
14865  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
14866  }
14867  else
14868  {
14869  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
14870  }
14871  }
14872  TTFile << '\n' << '\n';
14873 
14874  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
14875  {
14876  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
14877  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14878  {
14879  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
14880  {
14881  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
14882  }
14883  else
14884  {
14885  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
14886  }
14887  }
14888  TTFile << '\n';
14889  }
14890  TTFile << '\n' << '\n';
14891  }
14892 
14893  TTFile.close();
14894 
14895  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
14896 
14897  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
14898 
14899  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
14900 
14901  if(TTFile2 == 0)
14902  {
14903  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
14904  delete AllTTTrains;
14905  Utilities->CallLogPop(1710);
14906  return;
14907  }
14908 
14909  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
14910  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
14911  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
14912 
14913  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
14914  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
14915 
14916  // multimap of AnsiStrings with TimeString as key (to sort automatically)
14917 
14918  TTFile2 << TableTitle.c_str() << '\n' << '\n';
14919  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
14920  {
14921  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14922  {
14923  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
14924  {
14925  bool GiveMessagesFalse = false;
14926  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
14927  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
14928  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
14929  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
14930  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
14931  { // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
14932  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
14933  TimeString = TimeString.SubString(9, 5);
14934  ActionString += " " + OtherHeadCode;
14935  }
14936  if(TimeString.SubString(1, 7) == "End at ")
14937  // for Frh-sh final entry
14938  {
14939  TimeString = TimeString.SubString(8, 5);
14940  }
14941  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
14942  AnsiMultiMapEntry.first = TimeString;
14943  AnsiMultiMapEntry.second = OneLine;
14944  TAMM->insert(AnsiMultiMapEntry);
14945  }
14946  }
14947  }
14948 
14949  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
14950  {
14951  TTFile2 << (AMMIT->second).c_str();
14952  }
14953  delete AllTTTrains;
14954  delete TAMM;
14955  TTFile2.close();
14956  Utilities->CallLogPop(1580);
14957 }
14958 
14959 // ---------------------------------------------------------------------------
14960 
14961 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
14962  bool AtLocChecked, int ArrRange, int DepRange)
14963 {
14964 
14965  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
14966  bool AnalysisError = false;
14967 
14968  try
14969  {
14970  //New section after v2.4.3 for tt conflict analysis
14971  /*
14972  typedef std::list<AnsiString> TServiceCallingLocsList;
14973  typedef std::vector<TServiceCallingLocsList> TAllServiceCallingLocsMap;
14974 
14975  struct TLocServiceTimes
14976  {
14977  AnsiString Location;
14978  AnsiString ServiceAndRepeatNum;
14979  AnsiString AtLocTime;
14980  AnsiString ArrTime;
14981  AnsiString DepTime;
14982  };
14983  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
14984  */
14985 
14986  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
14987  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
14988 
14989  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
14990  TTrainDataVector::iterator TDVIt, TDVCopyIt;
14991  int Suffix = 0;
14992  int IteratorNumber = 0;
14993  AnsiString AnsiSuffix = "";
14994  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
14995  {
14996  IteratorNumber++; //first value in loop is 1
14997  Suffix = 0;
14998  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
14999  {
15000  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
15001  {
15002  Suffix++; //first value is 1
15003  AnsiSuffix = AnsiString(Suffix);
15004  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
15005  }
15006  }
15007  }
15008 
15009  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
15010  TServiceCallingLocsList ServiceCallingLocsList;
15011  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
15012  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15013  {
15014  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15015  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15016  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
15017  ServiceCallingLocsList.clear();
15018  if(ActionVector.empty())
15019  {
15020  continue;
15021  }
15022  if(ActionVector.at(0).SignallerControl)
15023  {
15024  continue;
15025  }
15026  for(unsigned int z = 0; z < ActionVector.size(); z++)
15027  {
15028  TActionVectorEntry AVE = ActionVector.at(z);
15029  if(AVE.FormatType == StartNew)
15030  {
15031  if(AVE.LocationType == AtLocation) //located Snt
15032  {
15033  ServiceCallingLocsList.push_back(AVE.LocationName);
15034  }
15035  else //unlocated Snt (could be entering at continuation)
15036  {
15038  if(TE.ActiveTrackElementName != "")
15039  {
15040  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
15041  }
15042  else
15043  {
15044  int HLoc = TE.HLoc;
15045  int VLoc = TE.VLoc;
15046  AnsiString HString;
15047  AnsiString VString;
15048  if(HLoc < 0)
15049  {
15050  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15051  }
15052  else
15053  {
15054  HString = AnsiString(HLoc);
15055  }
15056  if(VLoc < 0)
15057  {
15058  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15059  }
15060  else
15061  {
15062  VString = AnsiString(VLoc);
15063  }
15064  ServiceCallingLocsList.push_back(HString + '-' + VString);
15065  }
15066  }
15067  }
15068  else if(AVE.SequenceType == Start) //other start entries, all located
15069  {
15070  ServiceCallingLocsList.push_back(AVE.LocationName);
15071  }
15072  else if(AVE.FormatType == TimeLoc) //z must be > 0
15073  {
15074  if(ServiceCallingLocsList.back() != AVE.LocationName)
15075  {
15076  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
15077  }
15078  }
15079  else if(AVE.FormatType == PassTime)
15080  {
15081  ServiceCallingLocsList.push_back(AVE.LocationName);
15082  }
15083  else if(AVE.FormatType == TimeTimeLoc)
15084  {
15085  ServiceCallingLocsList.push_back(AVE.LocationName);
15086  }
15087  else if(AVE.Command == "cdt") //list if not next to start or finish
15088  {
15089  if(ActionVector.at(z-1).SequenceType == Start)
15090  {
15091  continue;
15092  }
15093  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
15094  {
15095  continue;
15096  }
15097  else
15098  {
15099  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
15100  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
15101  }
15102  }
15103  else if(AVE.FormatType == ExitRailway) //Fer
15104  {
15105  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
15106  AnsiString LName = TE.ActiveTrackElementName;
15107  if(LName != "")
15108  {
15109  ServiceCallingLocsList.push_back(LName);
15110  }
15111  else
15112  {
15113  int HLoc = TE.HLoc;
15114  int VLoc = TE.VLoc;
15115  AnsiString HString;
15116  AnsiString VString;
15117  if(HLoc < 0)
15118  {
15119  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15120  }
15121  else
15122  {
15123  HString = AnsiString(HLoc);
15124  }
15125  if(VLoc < 0)
15126  {
15127  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15128  }
15129  else
15130  {
15131  VString = AnsiString(VLoc);
15132  }
15133  ServiceCallingLocsList.push_back(HString + '-' + VString);
15134  }
15135  }
15136  }
15137  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
15138  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
15139  }
15140  //AllServiceCallingLocsMap built
15141 
15142  //test validity of AllServiceCallingLocsMap
15143 /*
15144  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15145  std::ofstream Test(TestFile.c_str());
15146 
15147  if(TestFile == 0)
15148  {
15149  ShowMessage("TestFile failed to open - can't be created");
15150  Utilities->CallLogPop();
15151  return false;
15152  }
15153 
15154  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
15155  {
15156  Test << ASCLIt->first << '\n'; //service ref
15157  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
15158  {
15159  Test << *SCLIt << '\n';
15160  }
15161  Test << "\n\n";
15162  }
15163  Test.close();
15164  Utilities->CallLogPop();
15165  return true;
15166 */
15167 
15168  //initialise variables before calc LastTTTime & build LocServiceTimesVector
15169  if(TrainDataVector.empty())
15170  {
15171  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
15172  Utilities->CallLogPop(2209);
15173  return false;
15174  }
15175  TLocServiceTimes TLSTEntry;
15176  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
15177  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
15178  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
15179  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
15180  LastTTTime = "";
15181 
15182  //calculate LastTTTime
15183  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15184  {
15185  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15186  TActionVector &ActionVector = TrainDataEntry.ActionVector;
15187  TActionVectorIterator AVLast = ActionVector.end() - 1;//points to last entry
15188  TDateTime LastTDTime;
15189  int IncMinutes = 0;
15190  NumTrains = TrainDataEntry.NumberOfTrains;
15191  if(ActionVector.empty())
15192  {
15193  continue;
15194  }
15195  if(ActionVector.at(0).SignallerControl)
15196  {
15197  continue;
15198  }
15199  if(AVLast->FormatType == Repeat)
15200  {
15201  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15202  AVLast--; //now points to the command before the repeat
15203  }
15204  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
15205  {
15206  AVLast--; //points to last timed entry
15207  }
15208  //here AVLast points to last entry with a time
15209  if(AVLast->ArrivalTime != TDateTime(-1))
15210  {
15211  LastTDTime = AVLast->ArrivalTime;
15212  }
15213  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
15214  {
15215  LastTDTime = AVLast->EventTime;
15216  }
15217  else
15218  {
15219  continue; //shouldn't ever reach here but if do then skip this service
15220  }
15221  if(NumTrains == 1)
15222  {
15223  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
15224  }
15225  else
15226  {
15227  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
15228  }
15229  if(LastAnsiTime > LastTTTime)
15230  {
15231  LastTTTime = LastAnsiTime;
15232  }
15233  }
15234 
15235 //build LocServiceTimesVector
15236 
15237 /* struct TLocServiceTimes
15238  {
15239  AnsiString Location;
15240  AnsiString ServiceAndRepeatNum;
15241  AnsiString AtLocTime;
15242  AnsiString ArrTime;
15243  AnsiString DepTime;
15244  };
15245  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15246 
15247 This works as follows:
15248 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
15249 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
15250 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
15251 
15252 Every action for every train is examined and times entered as follows:-
15253 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
15254 b) an unlocated Snt: entry time becomes DepTime
15255 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
15256 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
15257 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
15258 f) TimeTimeLoc: Arrival time entered as ErrTime, a check if Arr & Dep same and if s go in as one entry, else all minutes between entered as AtLocs then DepTime
15259 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
15260 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
15261 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
15262 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
15263 */
15264  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15265  {
15266  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15267  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15268  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
15269  int IncMinutes = 0;
15270  NumTrains = TrainDataEntry.NumberOfTrains;
15271  if(ActionVector.empty())
15272  {
15273  continue;
15274  }
15275  if(ActionVector.at(0).SignallerControl)
15276  {
15277  continue;
15278  }
15279  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15280  {
15281  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15282  }
15283 
15284  for(int y = 0; y < NumTrains; y++)
15285  {
15286  if(NumTrains == 1)
15287  {
15288  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
15289  }
15290  else if(y == 0)
15291  {
15292  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
15293  }
15294  else
15295  {
15296  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
15297  }
15298  for(unsigned int z = 0; z < ActionVector.size(); z++)
15299  {
15300  TActionVectorEntry AVE = ActionVector.at(z);
15301  TLSTEntry.AtLocTime = "";
15302  TLSTEntry.ArrTime = "";
15303  TLSTEntry.DepTime = "";
15304  TLSTEntry.Location = "";
15305  TLSTEntry.FrhMarker = "";
15306 
15307  if(AVE.FormatType == StartNew) //Snt only
15308  {
15309  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
15310  {
15311  TLSTEntry.Location = AVE.LocationName;
15312  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
15313  LocServiceTimesVector.push_back(TLSTEntry);
15314 
15315  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15316  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
15317  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15318  {
15319  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15320  {
15321  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
15322  break;
15323  }
15324  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15325  {
15326  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
15327  break;
15328  }
15329  }
15330  if(FoundStopTime == "")
15331  {
15332  throw Exception("Failure to determine FoundStopTime for located Snt");
15333  }
15334  int WhileCount = 0;
15335  while(true)
15336  {
15337  //add minutes until reach FoundStopTime but don't add that time
15338  WhileCount++;
15339  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15340  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15341  TLSTEntry.DepTime = "";
15342  TLSTEntry.ArrTime = "";
15343  if(IncTime >= FoundStopTime) //don't add that time
15344  {
15345  break;
15346  }
15347  LocServiceTimesVector.push_back(TLSTEntry);
15348  if(WhileCount > 2000)
15349  {
15350  throw Exception("While loop failed to break in 2000 loops for located Snt");
15351  }
15352  }
15353  }
15354  else //unlocated Snt, use the EventTime as DepTime for this vector
15355  {
15357  if(TE.ActiveTrackElementName != "")
15358  {
15359  TLSTEntry.Location = TE.ActiveTrackElementName;
15360  }
15361  else
15362  {
15363  int HLoc = TE.HLoc;
15364  int VLoc = TE.VLoc;
15365  AnsiString HString;
15366  AnsiString VString;
15367  if(HLoc < 0)
15368  {
15369  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15370  }
15371  else
15372  {
15373  HString = AnsiString(HLoc);
15374  }
15375  if(VLoc < 0)
15376  {
15377  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15378  }
15379  else
15380  {
15381  VString = AnsiString(VLoc);
15382  }
15383  TLSTEntry.Location = HString + '-' + VString;
15384  }
15385  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
15386  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
15387  LocServiceTimesVector.push_back(TLSTEntry);
15388  }
15389  }
15390 
15391  else if(AVE.SequenceType == Start) //other start entries, all located
15392  {
15393  TLSTEntry.Location = AVE.LocationName;
15394  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
15395  LocServiceTimesVector.push_back(TLSTEntry);
15396  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15397  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
15398  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15399  {
15400  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15401  {
15402  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
15403  break;
15404  }
15405  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15406  {
15407  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
15408  break;
15409  }
15410  }
15411  if(FoundStopTime == "")
15412  {
15413  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
15414  }
15415  int WhileCount = 0;
15416  while(true)
15417  {
15418  //add minutes until reach FoundStopTime but don't add that time
15419  WhileCount++;
15420  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15421  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15422  TLSTEntry.DepTime = "";
15423  TLSTEntry.ArrTime = "";
15424  if(IncTime >= FoundStopTime) //don't add that time
15425  {
15426  break;
15427  }
15428  LocServiceTimesVector.push_back(TLSTEntry);
15429  if(WhileCount > 2000)
15430  {
15431  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
15432  }
15433  }
15434  }
15435 
15436  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if ar5rival add in all mins to the departure or finish
15437  {
15438  TLSTEntry.Location = AVE.LocationName;
15439  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
15440  {
15441  bool SkipAddingMinutes = false;
15442  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
15443  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
15444  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
15445  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15446  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
15447  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15448  {
15449  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15450  {
15451  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
15452  break;
15453  }
15454  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15455  {
15456  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
15457  if((a == z + 1) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
15458  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
15459  {
15460  LocServiceTimesVector.pop_back();//pop the entry as the linked train will be listed at the relevant time and don't want to list both
15461  SkipAddingMinutes = true;
15462  }
15463  break;
15464  }
15465  }
15466  if(FoundStopTime == "")
15467  {
15468  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
15469  }
15470  if(!SkipAddingMinutes)
15471  {
15472  int WhileCount = 0;
15473  while(true)
15474  {
15475  //add minutes until reach FoundStopTime but don't add that time
15476  WhileCount++;
15477  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15478  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15479  TLSTEntry.DepTime = "";
15480  TLSTEntry.ArrTime = "";
15481  if(IncTime >= FoundStopTime) //don't add that time
15482  {
15483  break;
15484  }
15485  LocServiceTimesVector.push_back(TLSTEntry);
15486  if(WhileCount > 2000)
15487  {
15488  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
15489  }
15490  }
15491  }
15492  }
15493  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
15494  {
15495  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
15496  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
15497  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum))//if not it's a new service
15498  {
15499  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
15500  {
15501  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
15502  LocServiceTimesVector.pop_back();
15503  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
15504  }
15505  else //just add the dep & atloc times
15506  {
15507  TLSTEntry.ArrTime = "";
15508  LocServiceTimesVector.push_back(TLSTEntry);
15509  }
15510  }
15511  else //just add the dep & atloc times
15512  {
15513  TLSTEntry.ArrTime = "";
15514  LocServiceTimesVector.push_back(TLSTEntry);
15515  }
15516  }
15517  }
15518 
15519  else if(AVE.FormatType == TimeTimeLoc)
15520  {
15521  TLSTEntry.Location = AVE.LocationName;
15522  if(AVE.ArrivalTime > TDateTime(-1)) //should be
15523  {
15524  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
15525  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
15526  }
15527  if(AVE.DepartureTime > TDateTime(-1)) //should be
15528  {
15529  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
15530  }
15531  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
15532  {
15533  LocServiceTimesVector.push_back(TLSTEntry);
15534  }
15535  else
15536  {
15537  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
15538  TLSTEntry.DepTime = "";
15539  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
15540  TLSTEntry.ArrTime = ""; //done with this now
15541  while(TLSTEntry.AtLocTime < TempDepTime)
15542  {
15543  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15544  if(TLSTEntry.AtLocTime == TempDepTime)
15545  {
15546  TLSTEntry.DepTime = TempDepTime; //restore value
15547  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
15548  }
15549  else
15550  {
15551  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
15552  }
15553  }
15554  }
15555  }
15556 
15557  else if(AVE.FormatType == ExitRailway) //Fer
15558  {
15559  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
15560  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
15561  if(LName != "")
15562  {
15563  TLSTEntry.Location = LName;
15564  }
15565  else
15566  {
15567  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
15568  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
15569  AnsiString HString;
15570  AnsiString VString;
15571  if(HLoc < 0)
15572  {
15573  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15574  }
15575  else
15576  {
15577  HString = AnsiString(HLoc);
15578  }
15579  if(VLoc < 0)
15580  {
15581  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15582  }
15583  else
15584  {
15585  VString = AnsiString(VLoc);
15586  }
15587  TLSTEntry.Location = HString + '-' + VString;
15588  }
15589  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
15590  }
15591 
15592  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
15593  {
15594  AnsiString FrhTime;
15595  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15596  {
15597  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15598  }
15599  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15600  {
15601  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15602  }
15603  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
15604  TLSTEntry.Location = AVE.LocationName;
15605  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15606  TLSTEntry.FrhMarker = "Frh";
15607  LocServiceTimesVector.push_back(TLSTEntry);
15608  TLSTEntry.FrhMarker = "";
15609  //add all times from next minute to end of timetable
15610  while(IncTime <= LastTTTime)
15611  {
15612  TLSTEntry.AtLocTime = IncTime;
15613  LocServiceTimesVector.push_back(TLSTEntry);
15614  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
15615  }
15616  }
15617 
15618  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
15619  {
15620  if(y == NumTrains - 1)//last repeat, it remains here when accessed for the last train
15621  {
15622  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
15623  TLSTEntry.Location = AVE.LocationName;
15624  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15625  TLSTEntry.FrhMarker = "Frh";
15626  LocServiceTimesVector.push_back(TLSTEntry);
15627  TLSTEntry.FrhMarker = "";
15628  //add all times from next minute to end of timetable
15629  while(IncTime <= LastTTTime)
15630  {
15631  TLSTEntry.AtLocTime = IncTime;
15632  LocServiceTimesVector.push_back(TLSTEntry);
15633  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
15634  }
15635  }
15636  }
15637 
15638  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
15639  {
15640  //nothing is done here as the entry will be listed at this time under the new service reference
15641  }
15642  }
15643  }
15644  }
15645 
15646  //now sort in location order
15647  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
15648  //LocServiceTimesVector now complete & sorted in location order
15649 
15650  //declare pointers for use in printouts
15651  TLocServiceTimesVector::iterator Ptr1, Ptr2;
15652 
15653  //set up the output file
15654  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15655  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15656 
15657  std::ofstream TTFile3(TTFileName3.c_str());
15658 
15659  if(TTFile3 == 0)
15660  {
15661  ShowMessage("Conflict Analysis file failed to open - can't be created");
15662  Utilities->CallLogPop(2210);
15663  return false;
15664  }
15665 
15666  if(LocServiceTimesVector.empty())
15667  {
15668  ShowMessage("No timetabled services found");
15669  TTFile3.close();
15670  DeleteFile(TTFileName3);
15671  Utilities->CallLogPop(2211);
15672  return false;
15673  }
15674 
15675  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n\n\n";
15676 
15677 
15678  //arrivals
15679  if(ArrChecked)
15680  {
15681  //sort in ArrTime order for each location
15682  Ptr1 = LocServiceTimesVector.begin();
15683  Ptr2 = Ptr1 + 1;
15684  while(Ptr2 != LocServiceTimesVector.end())
15685  {
15686  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
15687  {
15688  Ptr2++;
15689  if(Ptr2 == LocServiceTimesVector.end())
15690  {
15691  break;
15692  }
15693  }
15694  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
15695  Ptr1 = Ptr2; //first entry with next name
15696  if(Ptr2 != LocServiceTimesVector.end())
15697  {
15698  Ptr2++;
15699  }
15700  }
15701 
15702  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
15703 
15704  TTFile3 << "Arrival analysis: an asterisk means that the number of same approach code arrivals is equal to or greater than the number of platforms.\n";
15705  TTFile3 << "If the total number of arrivals exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
15706  MinuteString = " minutes";
15707  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
15708  if(ArrRange == 1)
15709  {
15710  MinuteString = " minute";
15711  }
15712  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
15713  TTFile3 << ",Platforms,Trains\n\n";
15714 
15715  Ptr1 = LocServiceTimesVector.begin();
15716  Ptr2 = Ptr1 + 1;
15717  while(Ptr2 != LocServiceTimesVector.end())
15718  {
15719  PreviousService = "";
15720  NumTrainsAtLoc = 0;
15721  ServiceAndRepeatNumTotal = "";
15722  NumPlats = 0;
15723  NumPlatsAtThisLocCalculated = false;
15724  BasicTime = "";
15725  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
15726  {
15727  PreviousService = "";
15728  NumTrainsAtLoc = 0;
15729  ServiceAndRepeatNumTotal = "";
15730  NumPlats = 0;
15731  NumPlatsAtThisLocCalculated = false;
15732  BasicTime = "";
15733  Ptr1++;
15734  Ptr2++;
15735  if(Ptr2 == LocServiceTimesVector.end())
15736  {
15737  break;
15738  }
15739  }
15740  if(Ptr2 == LocServiceTimesVector.end())
15741  {
15742  break;
15743  }
15744  while(Ptr2->Location == Ptr1->Location)
15745  {
15746  PreviousService = "";
15747  NumTrainsAtLoc = 0;
15748  ServiceAndRepeatNumTotal = "";
15749  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
15750  if((Ptr1->Location == "") && (Ptr2->Location == ""))
15751  {
15752  break;
15753  }
15754  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
15755  {
15756  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
15757  Ptr1++;
15758  Ptr2++;
15759  if(Ptr2 == LocServiceTimesVector.end())
15760  {
15761  break;
15762  }
15763  if(Ptr2->Location != Ptr1->Location)
15764  {
15765  break;
15766  }
15767  }
15768  if(Ptr2 == LocServiceTimesVector.end())
15769  {
15770  break;
15771  }
15772  if(Ptr2->Location != Ptr1->Location)
15773  {
15774  break;
15775  }
15776  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
15777  {
15778  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
15779  {
15780  break;
15781  }
15782  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
15783  {
15784  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
15785  NumPlatsAtThisLocCalculated = true;
15786  }
15787  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
15788  {
15789  if(ServiceAndRepeatNumTotal == "")
15790  {
15791  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
15792  NumTrainsAtLoc = 1;
15793  }
15794  else
15795  {
15796  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
15797  }
15798  }
15799  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
15800  if(ServiceAndRepeatNumTotal == "")
15801  {
15802  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
15803  NumTrainsAtLoc = 1;
15804  }
15805  else
15806  {
15807  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
15808  }
15809  Ptr1 = Ptr2;
15810  Ptr2++;
15811  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
15812  {
15813  int MaxNumberOfSameDirections = 0;
15814  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
15815  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
15816  {
15817 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
15818  TTFile3.close();
15819  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
15820 // Utilities->CallLogPop(2224);
15821 // return false;
15822  }
15823  AnsiString Asterisk = "";
15824  if(MaxNumberOfSameDirections >= NumPlats)
15825  {
15826  Asterisk = "* ";
15827  }
15828  //print out a single line for number of trains at loc with all service refs
15829  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
15830  ArrivalsPrinted = true;
15831  ServiceAndRepeatNumTotal = "";
15832  }
15833  if(Ptr2 == LocServiceTimesVector.end())
15834  {
15835  break;
15836  }
15837  if(Ptr2->Location != Ptr1->Location)
15838  {
15839  break;
15840  }
15841  }
15842  if(Ptr2 == LocServiceTimesVector.end())
15843  {
15844  break;
15845  }
15846  }
15847  }
15848  if(!ArrivalsPrinted)
15849  {
15850  TTFile3 << "Nothing to report for arrivals";
15851  }
15852  TTFile3 << "\n\n\n";
15853  }
15854  //end of routine for arrivals
15855 
15856  //departures
15857  if(DepChecked)
15858  {
15859  //sort in DepTime order for each location
15860  Ptr1 = LocServiceTimesVector.begin();
15861  Ptr2 = Ptr1 + 1;
15862  while(Ptr2 != LocServiceTimesVector.end())
15863  {
15864  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
15865  {
15866  Ptr2++;
15867  if(Ptr2 == LocServiceTimesVector.end())
15868  {
15869  break;
15870  }
15871  }
15872  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
15873  Ptr1 = Ptr2; //first entry with next name
15874  if(Ptr2 != LocServiceTimesVector.end())
15875  {
15876  Ptr2++;
15877  }
15878  }
15879 
15880  //routine for departures - number of trains departing within the specified range with services listed at the end
15881  TTFile3 << "Departure analysis: an asterisk means that the number of same exit code departures is equal to or greater than the number of platforms.\n";
15882  TTFile3 << "If the total number of departures exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
15883  MinuteString = " minutes";
15884  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
15885  if(DepRange == 1)
15886  {
15887  MinuteString = " minute";
15888  }
15889  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
15890  TTFile3 << ",Platforms,Trains\n\n";
15891 
15892  Ptr1 = LocServiceTimesVector.begin();
15893  Ptr2 = Ptr1 + 1;
15894  while(Ptr2 != LocServiceTimesVector.end())
15895  {
15896  PreviousService = "";
15897  NumTrainsAtLoc = 0;
15898  ServiceAndRepeatNumTotal = "";
15899  NumPlats = 0;
15900  NumPlatsAtThisLocCalculated = false;
15901  BasicTime = "";
15902  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
15903  {
15904  PreviousService = "";
15905  NumTrainsAtLoc = 0;
15906  ServiceAndRepeatNumTotal = "";
15907  NumPlats = 0;
15908  NumPlatsAtThisLocCalculated = false;
15909  BasicTime = "";
15910  Ptr1++;
15911  Ptr2++;
15912  if(Ptr2 == LocServiceTimesVector.end())
15913  {
15914  break;
15915  }
15916  }
15917  if(Ptr2 == LocServiceTimesVector.end())
15918  {
15919  break;
15920  }
15921  while(Ptr2->Location == Ptr1->Location)
15922  {
15923  PreviousService = "";
15924  NumTrainsAtLoc = 0;
15925  ServiceAndRepeatNumTotal = "";
15926  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
15927  if((Ptr1->Location == "") && (Ptr2->Location == ""))
15928  {
15929  break;
15930  }
15931  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
15932  {
15933  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
15934  Ptr1++;
15935  Ptr2++;
15936  if(Ptr2 == LocServiceTimesVector.end())
15937  {
15938  break;
15939  }
15940  if(Ptr2->Location != Ptr1->Location)
15941  {
15942  break;
15943  }
15944  }
15945  if(Ptr2 == LocServiceTimesVector.end())
15946  {
15947  break;
15948  }
15949  if(Ptr2->Location != Ptr1->Location)
15950  {
15951  break;
15952  }
15953  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
15954  {
15955  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
15956  {
15957  break;
15958  }
15959  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
15960  {
15961  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
15962  NumPlatsAtThisLocCalculated = true;
15963  }
15964  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
15965  {
15966  if(ServiceAndRepeatNumTotal == "")
15967  {
15968  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
15969  NumTrainsAtLoc = 1;
15970  }
15971  else
15972  {
15973  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
15974  }
15975  }
15976  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
15977  if(ServiceAndRepeatNumTotal == "")
15978  {
15979  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
15980  NumTrainsAtLoc = 1;
15981  }
15982  else
15983  {
15984  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
15985  }
15986  Ptr1 = Ptr2;
15987  Ptr2++;
15988  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
15989  {
15990  int MaxNumberOfSameDirections = 0;
15991  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
15992  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
15993  {
15994 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
15995  TTFile3.close();
15996  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
15997 // Utilities->CallLogPop(2225);
15998 // return false;
15999  }
16000  AnsiString Asterisk = "";
16001  if(MaxNumberOfSameDirections >= NumPlats)
16002  {
16003  Asterisk = "* ";
16004  }
16005  //print out a single line for number of trains at loc with all service refs
16006  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16007  DeparturesPrinted = true;
16008  ServiceAndRepeatNumTotal = "";
16009  }
16010  if(Ptr2 == LocServiceTimesVector.end())
16011  {
16012  break;
16013  }
16014  if(Ptr2->Location != Ptr1->Location)
16015  {
16016  break;
16017  }
16018  }
16019  if(Ptr2 == LocServiceTimesVector.end())
16020  {
16021  break;
16022  }
16023  }
16024  }
16025  if(!DeparturesPrinted)
16026  {
16027  TTFile3 << "Nothing to report for departures";
16028  }
16029  TTFile3 << "\n\n\n";
16030  }
16031  //end of routine for departures
16032 
16033 
16034  //list trains at locations at same time
16035 
16036  if(AtLocChecked)
16037  {
16038  //sort in AtLocTime order for each location
16039  Ptr1 = LocServiceTimesVector.begin();
16040  Ptr2 = Ptr1 + 1;
16041  while(Ptr2 != LocServiceTimesVector.end())
16042  {
16043  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16044  {
16045  Ptr2++;
16046  if(Ptr2 == LocServiceTimesVector.end())
16047  {
16048  break;
16049  }
16050  }
16051  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
16052  Ptr1 = Ptr2; //first entry with next name
16053  if(Ptr2 != LocServiceTimesVector.end())
16054  {
16055  Ptr2++;
16056  }
16057  }
16058 
16059  //print out simultaneous AtLocs (don't need range of times for AtLocs)
16060  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
16061  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
16062  TTFile3 << ",Platforms,Trains,\n\n";
16063  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16064  Ptr1 = LocServiceTimesVector.begin();
16065  Ptr2 = Ptr1 + 1;
16066  while(Ptr2 != LocServiceTimesVector.end())
16067  {
16068  PreviousService = "";
16069  ServiceAndRepeatNumTotal = "";
16070  NumTrainsAtLoc = 0;
16071  NumPlats = 0;
16072  NumPlatsAtThisLocCalculated = false;
16073  FrhCount = 0;
16074  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16075  {
16076  PreviousService = "";
16077  ServiceAndRepeatNumTotal = "";
16078  NumTrainsAtLoc = 0;
16079  NumPlats = 0;
16080  NumPlatsAtThisLocCalculated = false;
16081  FrhCount = 0;
16082  Ptr1++;
16083  Ptr2++;
16084  if(Ptr2 == LocServiceTimesVector.end())
16085  {
16086  break;
16087  }
16088  }
16089  if(Ptr2 == LocServiceTimesVector.end())
16090  {
16091  break;
16092  }
16093  while(Ptr2->Location == Ptr1->Location)
16094  {
16095  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
16096  {
16097  FrhCount++;
16098  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16099  }
16100  PreviousService = "";
16101  NumTrainsAtLoc = 0;
16102  ServiceAndRepeatNumTotal = "";
16103  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16104  {
16105  break;
16106  }
16107  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
16108  {
16109  Ptr1++;
16110  if(Ptr1->FrhMarker == "Frh")
16111  {
16112  FrhCount++;
16113  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16114  }
16115  Ptr2++;
16116  if(Ptr2 == LocServiceTimesVector.end())
16117  {
16118  break;
16119  }
16120  if(Ptr2->Location != Ptr1->Location)
16121  {
16122  break;
16123  }
16124  }
16125  if(Ptr2 == LocServiceTimesVector.end())
16126  {
16127  break;
16128  }
16129  if(Ptr2->Location != Ptr1->Location)
16130  {
16131  break;
16132  }
16133  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
16134  {
16135  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
16136  {
16137  break;
16138  }
16139  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16140  {
16141  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
16142  NumPlatsAtThisLocCalculated = true;
16143  }
16144  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16145  {
16146  if(ServiceAndRepeatNumTotal == "")
16147  {
16148  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
16149  NumTrainsAtLoc = 1;
16150  }
16151  else
16152  {
16153  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
16154  }
16155  }
16156  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
16157  if(ServiceAndRepeatNumTotal == "")
16158  {
16159  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
16160  NumTrainsAtLoc = 1;
16161  }
16162  else
16163  {
16164  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
16165  }
16166  Ptr1 = Ptr2;
16167  if(Ptr1->FrhMarker == "Frh")
16168  {
16169  FrhCount++;
16170  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16171  }
16172  Ptr2++;
16173  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
16174  {
16175 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
16176 //new text //don't print out if all remainers or if only 1 train at loc
16177  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
16178 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
16179 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
16180  {
16181  AnsiString Asterisk = "";
16182  if(NumTrainsAtLoc > NumPlats)
16183  {
16184  Asterisk = "* ";
16185  }
16186  //print out a single line for number of trains at loc with all service refs
16187  if(FrhCount == 0)
16188  {
16189  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
16190  }
16191  else if(FrhCount == 1)
16192  {
16193  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
16194  }
16195  else
16196  {
16197  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
16198  }
16199  LastFrhCount = FrhCount;
16200  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
16201  AtLocsPrinted = true;
16202  ServiceAndRepeatNumTotal = "";
16203  }
16204  }
16205  if(Ptr2 == LocServiceTimesVector.end())
16206  {
16207  break;
16208  }
16209  if(Ptr2->Location != Ptr1->Location)
16210  {
16211  break;
16212  }
16213  }
16214  if(Ptr2 == LocServiceTimesVector.end())
16215  {
16216  break;
16217  }
16218  }
16219  }
16220  if(!AtLocsPrinted)
16221  {
16222  TTFile3 << "Nothing to report for trains at locations";
16223  }
16224  TTFile3 << "\n\n\n";
16225  //end of simultaneous AtLocs
16226 
16227 /*
16228  //print out the full vector here for testing purposes
16229  TTFile3 << "Full LocServiceTimesVector\n\n";
16230  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
16231 
16232  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
16233  {
16234  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
16235  }
16236 
16237  TTFile3 << "\n\n\n";
16238 */
16239  }
16240 
16241  TTFile3.close();
16242  Utilities->CallLogPop(2212);
16243  return true;
16244  }
16245 
16246  catch(const Exception &e)
16247  {
16248  AnsiString TTErrorFileName = "Analysis Error.txt";
16249  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
16250  std::ofstream TTError(TTErrorFileName.c_str());
16251  if(TTError == 0)
16252  {
16253  ShowMessage("Analysis error file failed to open - can't be created");
16254  Utilities->CallLogPop(2233);
16255  return false;
16256  }
16257  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16258  TTError << TimeNow.c_str() << "\n" << ArrRange << "\n" << ArrChecked << "\n" << DepRange << "\n" <<
16259  DepChecked << "\n" << AtLocChecked << "\n" << AnsiString(e.Message);
16260  TTError.close();
16261  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
16262  Utilities->CallLogPop(2226);
16263  return false;
16264  }
16265 }
16266 
16267 // ---------------------------------------------------------------------------
16268 
16269 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
16270 {
16271 //convert times to integer minutes
16272  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
16273  if((Time1 == "") || (Time2 == ""))
16274  {
16275  Utilities->CallLogPop(2213);
16276  return false;
16277  }
16278  int Mins = Time1.SubString(4,2).ToInt();
16279  int Hours = Time1.SubString(1,2).ToInt();
16280  int Time1Mins = (Hours * 60) + Mins;
16281  Mins = Time2.SubString(4,2).ToInt();
16282  Hours = Time2.SubString(1,2).ToInt();
16283  int Time2Mins = (Hours * 60) + Mins;
16284  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
16285  {
16286  Utilities->CallLogPop(2214);
16287  return true;
16288  }
16289  Utilities->CallLogPop(2215);
16290  return false;
16291 }
16292 
16293 // ---------------------------------------------------------------------------
16294 
16295 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
16296  bool &AnalysisError, int &MaxNumberOfSameDirections)
16297 {//input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
16298 
16299  try
16300  {
16301  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
16302  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
16303  int SCPos = 0;
16304  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
16305  //first change every second comma in Input to a semicolon so can separate services but keep times with services
16306  bool EvenComma = false;
16307  for(int x = 1; x <= Input.Length(); x++)
16308  {
16309  TempStr1 = Input[x];
16310  if(TempStr1 == AnsiString(',') && EvenComma)
16311  {
16312  TempStr2 += ';';
16313  }
16314  else
16315  {
16316  TempStr2 += Input[x];
16317  }
16318  if(TempStr1 == AnsiString(','))
16319  {
16320  EvenComma = !EvenComma;
16321  }
16322  }
16323  //load up the list of services with associated times
16324  while(TempStr2.Length() > 0)
16325  {
16326  SCPos = TempStr2.Pos(';');
16327  if(SCPos > 0) //0 if not found, as won't be when only one service left
16328  {
16329  OneService = TempStr2.SubString(1, SCPos - 1);
16330  ServiceList.push_back(OneService);
16331  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
16332  }
16333  else //no semicolon so looking at last (or only) element
16334  {
16335  ServiceList.push_back(TempStr2);
16336  TempStr2 = "";
16337  }
16338  }
16339  ServiceList.sort(); // alphabetical order
16340  ServiceList.unique(); //remove duplicates
16341  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
16342 
16343  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
16344  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
16345  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
16346  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
16347 
16348  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16349  {
16350  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
16351  }
16352  SLIt3 = ServiceList.end();
16353  SLIt3--; //so points to last element
16354  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
16355  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
16356  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
16357  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
16358  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
16359  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
16360 
16361  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
16362  {
16363  SLIt = SLIt1;
16364  SLIt++; //so points to one after SLIt1
16365  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
16366  {
16367  continue; //already allocated so skip to the next
16368  }
16369  else
16370  {
16371  CommaPos1 = SLIt1->Pos(','); //can't be 0
16372  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
16373  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
16374  SpacePos = ServiceRef1.Pos(' ');
16375  RepeatNum1 = 0;
16376  if(SpacePos > 0) //otherwise it's already correct
16377  {
16378  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
16379  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
16380  if(RepeatInfo1[1] == 'F')
16381  {
16382  RepeatNum1 = 0;
16383  }
16384  else
16385  {
16386  SpacePos = RepeatInfo1.Pos(' ');
16387  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
16388  }
16389  }
16390  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
16391  //but this includes the "&0" etc so need to strip these
16392  AmpersandPos = AnsiTime1.Pos('&');
16393  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
16394 
16395  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
16396  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
16397  {
16398  throw Exception("ASCLIt1 Error in " + Input);
16399  }
16400  ServiceCallingLocsList1 = ASCLIt1->second;
16401  AmpersandPos = SLIt1->Pos('&');
16402  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16403  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
16404 
16405  SameDirectionCount = 1;
16406  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
16407  {
16408  CommaPos2 = SLIt2->Pos(','); //can't be 0
16409  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
16410  //but this contains "(First service..." etc so need to strip these
16411  SpacePos = ServiceRef2.Pos(' ');
16412  RepeatNum2 = 0;
16413  if(SpacePos > 0) //otherwise it's already correct
16414  {
16415  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
16416  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
16417  if(RepeatInfo2[1] == 'F')
16418  {
16419  RepeatNum2 = 0;
16420  }
16421  else
16422  {
16423  SpacePos = RepeatInfo2.Pos(' ');
16424  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
16425  }
16426 
16427  }
16428  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
16429  //but this includes the "&0" etc so need to strip these
16430  AmpersandPos = AnsiTime2.Pos('&');
16431  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
16432 
16433  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
16434  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
16435  {
16436  throw Exception("ASCLIt2 Error in " + Input);
16437  }
16438  ServiceCallingLocsList2 = ASCLIt2->second;
16439  //now compare the two
16440  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
16441  {
16442  int AmpersandPos = SLIt2->Pos('&');
16443  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16444  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
16445  SameDirectionCount++;
16446  }
16447  }
16448  if(SameDirectionCount > MaxNumberOfSameDirections)
16449  {
16450  MaxNumberOfSameDirections = SameDirectionCount;
16451  }
16452  }
16453  }
16454 
16455  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0"))//*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
16456  { //any existing direction so allocate it now
16457  AmpersandPos = SLIt3->Pos('&');
16458  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16459  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
16460  }
16461 
16462  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
16463  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16464  {
16465  //extract the DirectionMarker as an integer
16466  AmpersandPos = SLIt->Pos('&');
16467  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
16468  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
16469  DirectionMarker = DirectionMarkerString.ToInt();
16470  AnsiString DirectionSuffix = "";
16471  char c;
16472  if(DirectionMarker < 27)
16473  {
16474  c = 64 + DirectionMarker; //so 1 -> 'A'
16475  DirectionSuffix = "," + AnsiString(c);
16476  }
16477  else if(DirectionMarker < 53)
16478  {
16479  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
16480  DirectionSuffix = ",A" + AnsiString(c);
16481  }
16482  else
16483  {
16484  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
16485  }
16486  *SLIt = ServiceWithoutMarker + DirectionSuffix;
16487  }
16488  //now prepare the final consolidated output
16489  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16490  {
16491  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
16492  }
16493  if(Output.Length() > 0)
16494  {
16495  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
16496  }
16497  Utilities->CallLogPop(2216);
16498  return Output;
16499  }
16500 
16501  catch(const Exception &e)
16502  {
16503  AnalysisError = true;
16504  Utilities->CallLogPop(2227);
16505  return e.Message;
16506  }
16507 }
16508 
16509 // ---------------------------------------------------------------------------
16510 
16511 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
16512 {//similar to above but doesn't include times in the input
16513  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
16514  AnsiString InternalInput = Input, Output = "", OneService = "";
16515  int CommaPos = 0;
16516  std::list<AnsiString> ServiceList;
16517  //load up the list
16518  while(InternalInput.Length() > 0)
16519  {
16520  CommaPos = InternalInput.Pos(',');
16521  if(CommaPos > 0) //0 if not found, as won't be when only one service left
16522  {
16523  OneService = InternalInput.SubString(1, CommaPos - 1);
16524  ServiceList.push_back(OneService);
16525  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
16526  }
16527  else //no comma so looking at last (or only) element
16528  {
16529  ServiceList.push_back(InternalInput);
16530  InternalInput = "";
16531  }
16532  }
16533 
16534  ServiceList.sort(); // alphabetical order
16535  ServiceList.unique(); //remove duplicates
16536  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
16537  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16538  {
16539  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
16540  }
16541  if(Output.Length() > 0)
16542  {
16543  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
16544  }
16545  Utilities->CallLogPop(2217);
16546  return Output;
16547 }
16548 
16549 // ---------------------------------------------------------------------------
16550 
16551 
16552 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
16553  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
16554  {
16555  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
16556  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
16557 
16558  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
16559  //general list pointers, LocPtrs point to Location in the two lists
16560 
16561  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
16562  //for List1
16563  bool LocFound = false;
16564  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
16565  int IncMinutes;
16566  TDateTime FirstServiceTime;
16567 
16568  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
16569  int Ref1Target = 0, Ref1Count = 0;
16570  int SlashPos = Ref1.Pos('/');
16571  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
16572  {
16573  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
16574  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
16575  }
16576  int Ref2Target = 0, Ref2Count = 0;
16577  SlashPos = Ref2.Pos('/');
16578  if(SlashPos > 0) //if 0 leave as is
16579  {
16580  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
16581  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
16582  }
16583 
16584  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
16585  { //even if others have same names. But if there are cdt's then need to refind the correct service
16586  if((*ListPtr1) == Location) //
16587  {
16588  LocPtr1 = ListPtr1; //may be modified later
16589  LocFound = true;
16590  }
16591  if(ListPtr1->SubString(1, 3) == "%%%")
16592  {
16593  AnsiString CDTTime = ListPtr1->SubString(4, 5);
16594  //now adjust the time to correspond to the repeat if there is one
16595  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
16596  {
16597  IncMinutes = -1;
16598  FirstServiceTime = TDateTime(-1);
16599  bool BreakFlag = false;
16600  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
16601  {
16602  if(TDVIt->ServiceReference == Ref1)
16603  {
16604  if(Ref1Target > Ref1Count)
16605  {
16606  Ref1Count++;
16607  continue;
16608  }
16609  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
16610  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
16611  {
16612  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
16613  {
16614  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
16615  BreakFlag = true;
16616  break;
16617  }
16618  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
16619  {
16620  FirstServiceTime = AVIt->ArrivalTime;
16621  BreakFlag = true;
16622  break;
16623  }
16624  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
16625  {
16626  FirstServiceTime = AVIt->DepartureTime;
16627  BreakFlag = true;
16628  break;
16629  }
16630  }
16631  if(BreakFlag)
16632  {
16633  break;
16634  }
16635  }
16636  }
16637  if(IncMinutes == -1)
16638  {
16639  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16640  }
16641  if(FirstServiceTime == TDateTime(-1))
16642  {
16643  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16644  }
16645  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
16646  }
16647  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
16648  {
16649  LocFound = false;
16650  continue;
16651  }
16652  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
16653  {
16654  break;
16655  }
16656  if(Time1 > CDTTime) //not there yet so go on
16657  {
16658  LocFound = false;
16659  continue;
16660  }
16661  if(Time1 < CDTTime) //gone too far so can stop now
16662  {
16663  break;
16664  }
16665  }
16666  }
16667  if(!LocFound) //have to find it in both lists
16668  {
16669  Utilities->CallLogPop(2228);
16670  return false;
16671  }
16672 
16673  //for List2
16674  LocFound = false;
16675  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
16676  {
16677  if((*ListPtr2) == Location)
16678  {
16679  LocPtr2 = ListPtr2; //may be modified later
16680  LocFound = true;
16681  }
16682  if(ListPtr2->SubString(1, 3) == "%%%")
16683  {
16684  AnsiString CDTTime = ListPtr2->SubString(4, 5);
16685  //now adjust the time to correspond to the repeat if there is one
16686  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
16687  {
16688  IncMinutes = -1;
16689  FirstServiceTime = TDateTime(-1);
16690  bool BreakFlag = false;
16691  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
16692  {
16693  if(TDVIt->ServiceReference == Ref2)
16694  {
16695  if(Ref2Target > Ref2Count)
16696  {
16697  Ref2Count++;
16698  continue;
16699  }
16700  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
16701  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
16702  {
16703  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
16704  {
16705  FirstServiceTime = AVIt->EventTime;
16706  BreakFlag = true;
16707  break;
16708  }
16709  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
16710  {
16711  FirstServiceTime = AVIt->ArrivalTime;
16712  BreakFlag = true;
16713  break;
16714  }
16715  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
16716  {
16717  FirstServiceTime = AVIt->DepartureTime;
16718  BreakFlag = true;
16719  break;
16720  }
16721  }
16722  if(BreakFlag)
16723  {
16724  break;
16725  }
16726  }
16727  }
16728  if(IncMinutes == -1)
16729  {
16730  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16731  }
16732  if(FirstServiceTime == TDateTime(-1))
16733  {
16734  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16735  }
16736  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
16737  }
16738  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
16739  {
16740  LocFound = false;
16741  continue;
16742  }
16743  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
16744  {
16745  break;
16746  }
16747  if(Time2 > CDTTime) //not there yet so go on
16748  {
16749  LocFound = false;
16750  continue;
16751  }
16752  if(Time2 < CDTTime) //gone too far so can stop now
16753  {
16754  break;
16755  }
16756  }
16757  }
16758  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
16759  {
16760  Utilities->CallLogPop(2229);
16761  return false;
16762  }
16763 
16764  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
16765  //set ListPtr1 to the search start position
16766  if(Arrival)
16767  {
16768  LP1 = List1.begin();
16769  LP1--; //now points to before the first entry
16770  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
16771  {
16772  if(ListPtr1 == List1.begin())
16773  {
16774  break;
16775  }
16776  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
16777  {
16778  ListPtr1++; //point to one past the cdt
16779  break;
16780  }
16781  }
16782  //set ListPtr2 to the search start position
16783  LP2 = List2.begin();
16784  LP2--; //now points to before the first entry
16785  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
16786  {
16787  if(ListPtr2 == List2.begin())
16788  {
16789  break;
16790  }
16791  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
16792  {
16793  ListPtr2++; //point to one past the cdt
16794  break;
16795  }
16796  }
16797  //ListPtr1 & 2 now at search start position
16798  LP1 = ListPtr1;
16799  LP2 = ListPtr2;
16800  //now search forwards, i.e. for common locations before Location
16801  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
16802  {
16803  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
16804  {
16805  break;
16806  }
16807  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
16808  {
16809  break;
16810  }
16811  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
16812  {
16813  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
16814  {
16815  break;
16816  }
16817  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
16818  {
16819  break;
16820  }
16821  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
16822  {
16823  Utilities->CallLogPop(2230);
16824  return true;
16825  }
16826  }
16827  }
16828  }
16829 
16830  //now, for the departure analysis, reset the start positions and search locations after Location
16831 
16832  else
16833  {
16834  LP1 = LocPtr1; LP1++; //start at one past the location itself
16835  LP2 = LocPtr2; LP2++;
16836  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
16837  {
16838  if(ListPtr1 == List1.end()) //reached end point so stop
16839  {
16840  break;
16841  }
16842  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
16843  {
16844  break;
16845  }
16846  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
16847  {
16848  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
16849  {
16850  break;
16851  }
16852  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
16853  {
16854  break;
16855  }
16856  if((*ListPtr1) == (*ListPtr2)) //found a common later location
16857  {
16858  Utilities->CallLogPop(2231);
16859  return true;
16860  }
16861  }
16862  }
16863  }
16864  Utilities->CallLogPop(2232);
16865  return false;
16866  }
16867 
16868 // ---------------------------------------------------------------------------
16869 
16870 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TExitList &ExitList) const
16871 {
16872  // check all timetable names in list, if all same return " at [name]" else return ""
16873  if(ExitList.empty())
16874  return "";
16875 
16876  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
16877  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
16878 
16879  if(StartName == "")
16880  {
16881  if(ExitList.size() == 1)
16882  {
16883  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
16884  Utilities->CallLogPop(1571);
16885  return " at " + ID;
16886  }
16887  else
16888  {
16889  Utilities->CallLogPop(1572);
16890  return "";
16891  }
16892  }
16893  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
16894  {
16895  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
16896  {
16897  Utilities->CallLogPop(1570);
16898  return "";
16899  }
16900  }
16901  Utilities->CallLogPop(1569);
16902  return " at " + StartName;
16903 }
16904 
16905 // ---------------------------------------------------------------------------
16906 /* can't trust this as locations within a vector may not be contiguous
16907  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
16908  {
16909  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
16910  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
16911  //must be preceded by a TimeLoc departure
16912  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
16913  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
16914  {
16915  if((AVPtr + x) < TDEPtr->ActionVector.end())
16916  {
16917  AnsiString xx = (AVPtr + x)->Command;//test
16918  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
16919  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
16920  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
16921  {
16922  Utilities->CallLogPop();
16923  return false;
16924  }
16925  else if((AVPtr + x)->SequenceType == Finish)
16926  {
16927  Utilities->CallLogPop();
16928  return true;
16929  }
16930  }
16931  }
16932  Utilities->CallLogPop();
16933  return false;
16934  }
16935 */
16936 // ---------------------------------------------------------------------------
16937 
16938 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
16939 {
16940  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
16941  AnsiString FormatStr = "####0.0";
16942  AnsiString AvLateArrMins = "";
16943  AnsiString AvEarlyArrMins = "";
16944  AnsiString AvLatePassMins = "";
16945  AnsiString AvEarlyPassMins = "";
16946  AnsiString AvLateDepMins = "";
16947 
16948  if(LateArrivals > 0)
16949  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
16950  if(EarlyArrivals > 0)
16951  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
16952  if(LatePasses > 0)
16953  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
16954  if(EarlyPasses > 0)
16955  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
16956  if(LateDeps > 0)
16957  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
16958 
16959  PerfFile << '\n' << '\n' << "***************************************";
16960  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
16961 
16962  if(OnTimeArrivals != 1)
16963  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
16964  else
16965  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
16966 
16967  if(LateArrivals > 1)
16968  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
16969  else if(LateArrivals == 1)
16970  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
16971  else
16972  PerfFile << LateArrivals << " late arrivals" << '\n';
16973 
16974  if(EarlyArrivals > 1)
16975  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
16976  else if(EarlyArrivals == 1)
16977  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
16978  else
16979  PerfFile << EarlyArrivals << " early arrivals" << '\n';
16980 
16981  if(OnTimePasses != 1)
16982  PerfFile << OnTimePasses << " on-time passes" << '\n';
16983  else
16984  PerfFile << OnTimePasses << " on-time pass" << '\n';
16985 
16986  if(LatePasses > 1)
16987  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
16988  else if(LatePasses == 1)
16989  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
16990  else
16991  PerfFile << LatePasses << " late passes" << '\n';
16992 
16993  if(EarlyPasses > 1)
16994  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
16995  else if(EarlyPasses == 1)
16996  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
16997  else
16998  PerfFile << EarlyPasses << " early passes" << '\n';
16999 
17000  if(OnTimeDeps != 1)
17001  PerfFile << OnTimeDeps << " on-time departures" << '\n';
17002  else
17003  PerfFile << OnTimeDeps << " on-time departure" << '\n';
17004 
17005  if(LateDeps > 1)
17006  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
17007  else if(LateDeps == 1)
17008  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
17009  else
17010  PerfFile << LateDeps << " late departures" << '\n';
17011 
17012  TDateTime TempExcessLCDownTime;
17013  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
17014  {
17015  if(Track->BarriersDownVector.at(x).ReducedTimePenalty)
17016  {
17017  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
17018  }
17019  else
17020  {
17021  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
17022  }
17023  if(TempExcessLCDownTime > TDateTime(0))
17024  {
17025  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
17026  }
17027  }
17028 
17029  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
17030 
17031  if(ExcessLCDownMins > 0.1)
17032  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
17033 
17034  if(MissedStops != 1)
17035  PerfFile << MissedStops << " missed stops" << '\n';
17036  else
17037  PerfFile << MissedStops << " missed stop" << '\n';
17038 
17039  if(OtherMissedEvents != 1)
17040  PerfFile << OtherMissedEvents << " other missed events" << '\n';
17041  else
17042  PerfFile << OtherMissedEvents << " other missed event" << '\n';
17043 
17044  if(UnexpectedExits != 1)
17045  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
17046  else
17047  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
17048 
17049  if(IncorrectExits != 1)
17050  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
17051  else
17052  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
17053 
17054  if(NumFailures != 1)
17055  PerfFile << NumFailures << " train failures" << '\n';
17056  else
17057  PerfFile << NumFailures << " train failure" << '\n';
17058 
17059  if(AvHoursIntValue > 0)
17060  {
17061  if(AvHoursIntValue == 1)
17062  {
17063  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
17064  }
17065  else
17066  {
17067  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
17068  }
17069  }
17070 
17071  AnsiString AvLateMinsLocsNotReached = "";
17072 
17074  int LocsNotReached = (NotStartedTrainArrDep + OperatingTrainArrDep) / 2;
17075 
17076  // each location has an arrival and departure (generally) so divide by 2
17077  if(LocsNotReached > 0)
17078  {
17079  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainArrDep + OperatingTrainArrDep));
17080  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
17081  }
17082 
17083  if(SPADRisks != 1)
17084  PerfFile << SPADRisks << " SPAD risks" << '\n';
17085  else
17086  PerfFile << SPADRisks << " SPAD risk" << '\n';
17087 
17088  if(SPADEvents != 1)
17089  PerfFile << SPADEvents << " SPADs" << '\n';
17090  else
17091  PerfFile << SPADEvents << " SPAD" << '\n';
17092 
17093  if(Derailments != 1)
17094  PerfFile << Derailments << " derailments" << '\n';
17095  else
17096  PerfFile << Derailments << " derailment" << '\n';
17097 
17098  if(CrashedTrains != 1)
17099  PerfFile << CrashedTrains << " crashed trains" << '\n';
17100  else
17101  PerfFile << CrashedTrains << " crashed train" << '\n';
17102 
17103  PerfFile << '\n' << "***************************************" << '\n';
17104 
17105  bool DerailSPADFlag = false, CrashFlag = false;
17106 
17107  int OverallScorePercent = 100;
17108  int TotArrDep = 0;
17109  double TotLateMinsFactor = 1;
17110  double MissedStopAndSPADRiskFactor = 1;
17111  double NetNegFactor = 1;
17112 
17114  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
17115  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
17116  // 'no timetabled departures... message, which was inappropriate
17117 
17118  if((SPADEvents > 0) || (Derailments > 0))
17119  {
17120  OverallScorePercent = 5; // overrides other calculations
17121  DerailSPADFlag = true;
17122  }
17123  if(CrashedTrains > 0)
17124  {
17125  OverallScorePercent = 0; // overrides other calculations
17126  CrashFlag = true;
17127  }
17128  if(OverallScorePercent == 100)
17129  {
17130  if(TotArrDep > 0)
17131  {
17132  TotLateMinsFactor =
17134  ((OtherMissedEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDep);
17135  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
17136  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
17137  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDep);
17138  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
17139  // of arrivals & departures, where 4% = half, 8% = a quarter etc
17140  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
17141  // NetNegfactor: product of the above two
17142  OverallScorePercent = 100 * NetNegFactor;
17143  }
17144  }
17145  if((TotArrDep > 0) || DerailSPADFlag || CrashFlag)
17146  // flag condits added at v1.1.4 - see above for what the error was
17147  {
17148  AnsiString OneFailureString = ", though the failure would account for some poor performance";
17149  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
17150  AnsiString AddedString = "";
17151  if(NumFailures == 1)
17152  AddedString = OneFailureString;
17153  if(NumFailures > 1)
17154  AddedString = TwoOrMoreFailureString;
17155  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
17156  AnsiString Rating = "";
17157  if(OverallScorePercent == 100)
17158  Rating = "Perfect!";
17159  else if(OverallScorePercent >= 95)
17160  Rating = "Excellent";
17161  else if(OverallScorePercent >= 90)
17162  Rating = "Very good";
17163  else if(OverallScorePercent >= 80)
17164  Rating = "Good";
17165  else if(OverallScorePercent >= 70)
17166  Rating = "Fair";
17167  else if(OverallScorePercent >= 60)
17168  Rating = "Unacceptable" + AddedString;
17169  else if(OverallScorePercent >= 50)
17170  Rating = "Poor" + AddedString;
17171  else if(OverallScorePercent >= 40)
17172  Rating = "Bad" + AddedString;
17173  else if(OverallScorePercent >= 30)
17174  Rating = "Very bad" + AddedString;
17175  else if(OverallScorePercent >= 20)
17176  Rating = "Terrible" + AddedString;
17177  else if(OverallScorePercent >= 10)
17178  Rating = "Appalling" + AddedString;
17179  else if(OverallScorePercent >= 5)
17180  {
17181  if(DerailSPADFlag)
17182  Rating = "Disastrous - potential loss of life";
17183  // SPADs/Derailments
17184  else
17185  Rating = "Dire" + AddedString;
17186  }
17187  else if(OverallScorePercent < 5)
17188  {
17189  if(CrashFlag)
17190  Rating = "Catastrophic - loss of life"; // Crashes
17191  else
17192  Rating = "Abysmal";
17193  }
17194  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
17195  }
17196  else
17197  {
17198  PerfFile << "\nThere were no timetabled departures or arrivals so there is insufficient information to provide a performance score or rating" << '\n';
17199  }
17200  PerfFile << '\n' << "***************************************";
17201  Utilities->CallLogPop(1736);
17202 }
17203 
17204 // ---------------------------------------------------------------------------
17205 
17207 {
17208  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
17209  for(unsigned int x = 0; x < TrainVector.size(); x++)
17210  {
17211  TTrain &Train = TrainVectorAt(58, x);
17212  if(Train.Crashed)
17213  // can't use background colours for crashed & derailed because same colour
17214  {
17215  CrashWarning = true;
17216  }
17217  else if(Train.Derailed)
17218  // can't use background colours for crashed & derailed because same colour
17219  {
17220  DerailWarning = true;
17221  }
17222  else if(Train.BackgroundColour == clSPADBackground)
17223  // use colour as that changes as soon as passes signal
17224  {
17225  SPADWarning = true;
17226  }
17227  else if(Train.BackgroundColour == clTrainFailedBackground)
17228  {
17229  TrainFailedWarning = true;
17230  }
17231  else if(Train.BackgroundColour == clCallOnBackground)
17232  // use colour as also stopped at signal
17233  {
17234  CallOnWarning = true;
17235  }
17236  else if(Train.BackgroundColour == clSignalStopBackground)
17237  // use colour to distinguish from call-on
17238  {
17239  SignalStopWarning = true;
17240  }
17241  else if(Train.BackgroundColour == clBufferAttentionNeeded)
17242  // use colour to distinguish from ordinary buffer stop
17243  {
17244  BufferAttentionWarning = true;
17245  }
17246  }
17247  Utilities->CallLogPop(1796);
17248 }
17249 
17250 // ---------------------------------------------------------------------------
17251 
17253 {
17254  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
17255 
17256  // calculate lateness for running trains
17259  for(unsigned int x = 0; x < TrainVector.size(); x++)
17260  {
17261  TTrain &Train = TrainVectorAt(64, x);
17262  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
17263  AVEntryPtr++)
17264  {
17265  if(AVEntryPtr < Train.ActionVectorEntryPtr)
17266  continue;
17267  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
17268  TTClockTime))
17269  {
17270  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
17272  }
17273  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
17274  TTClockTime))
17275  {
17276  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber,
17277  Train.IncrementalMinutes));
17279  }
17280  }
17281  }
17282 
17283  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
17286 
17287  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17288  {
17289  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
17290  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
17291  int IncrementalMinutes = 0;
17292  if(AVEntryLast.FormatType == Repeat)
17293  {
17294  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
17295  }
17296  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
17297  {
17298  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
17299  if(TTOD.RunningEntry != NotStarted)
17300  continue;
17301  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
17302  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
17303  bool TrainOperatingFlag = false;
17304  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
17305  {
17306  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
17307  {
17308  TrainOperatingFlag = true;
17309  break;
17310  }
17311  }
17312  if(TrainOperatingFlag)
17313  continue;
17314 
17315  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
17316  {
17317  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
17318  }
17319  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
17320  {
17321  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
17322  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
17323  {
17324  break; // all the rest will also be greater (& default of -1 will be less)
17325  }
17326  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
17327  {
17328  break; // all the rest will also be greater (& default of -1 will be less)
17329  }
17330  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
17331  {
17332  break; // all the rest will also be greater (& default of -1 will be less)
17333  }
17334  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
17335  {
17336  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
17338  }
17339  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
17340  {
17341  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
17343  }
17344  }
17345  }
17346  }
17347  Utilities->CallLogPop(1894);
17348 }
17349 
17350 // ---------------------------------------------------------------------------
17351 
17353  // new v2.2.0 for OperatorActionPanel
17354  // clears entries then adds values for running trains then for continuation entries
17355  // dont limit size here as need to check all trains (OAListBox is limites to 20 trains in Interface.cpp)
17356 {
17357  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
17358  OpTimeToActMultiMap.clear();
17359  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
17360 
17361  if(!TrainVector.empty())
17362  // build OpTimeToActMultiMap entries for running trains
17363  {
17364  AnsiString HeadCode;
17365  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
17366  int TrainID;
17367  THCandTrainPosParam HCandTrainPosParam;
17368  for(unsigned int x = 0; x < TrainVector.size(); x++)
17369  {
17370  HeadCode = TrainVectorAt(62, x).HeadCode;
17371  TrainID = TrainVectorAt(63, x).TrainID;
17372  HCandTrainPosParam.first = HeadCode;
17373  HCandTrainPosParam.second = TrainID;
17374  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
17375  if((TimeToAct >= 0) && (TimeToAct < 59.9))
17376  // -1 indicates don't display
17377  {
17378  OpTimeToActMultiMapEntry.first = TimeToAct;
17379  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
17380  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
17381  }
17382  }
17383  }
17384 
17385 /*
17386  * class TContinuationTrainExpectationEntry
17387  {
17388  public:
17389  AnsiString Description; ///< service description
17390  AnsiString HeadCode; ///< service headcode
17391  int RepeatNumber; ///< service RepeatNumber
17392  int IncrementalMinutes; ///< Repeat separation in minutes
17393  int IncrementalDigits; ///< Repeat headcode separation
17394  int VectorPosition; ///< TrackVectorPosition for the continuation element
17395  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
17396  };
17397 
17398  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
17399  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
17400  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
17401  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
17402 */
17403 
17405  // build OpTimeToActMultiMap entries for expected trains
17406  {
17407  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
17408  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
17409  float TimeToAct = 0; // minutes
17410  int DistanceToRedSignal = 0; // metres
17411  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
17412  // used to ensure only one train displayed for a given continuation
17413  ContinuationEntryVecPosVector.clear();
17414  bool LaterTrain = false;
17417  {
17418  LaterTrain = false;
17419  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
17420  {
17421  CTEIt++;
17422  continue; // not interested in running or exited trains
17423  }
17424  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
17425  {
17426  CTEIt++;
17427  continue;
17428  // don't include trains not entered yet when a train is already on the continuation
17429  }
17430  if(!ContinuationEntryVecPosVector.empty())
17431  {
17432  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
17433  {
17434  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
17435  {
17436  LaterTrain = true; ;
17437  // skip past remaining trains waiting to enter at same point
17438  break;
17439  }
17440  }
17441  }
17442  if(LaterTrain)
17443  {
17444  CTEIt++;
17445  continue;
17446  }
17447  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
17448  AnsiString HeadCode = CTEIt->second.HeadCode;
17449  float CurrentStopTime; // set to 0 at start of function
17450  float LaterStopTime; // set to 0 at start of function
17451  float RecoverableTime; // set to 0 at start of function
17452  int AvTrackSpeed; // set to 0 at start of function
17453  int TrainID = -1; // not yet allocated for train still to enter
17454  bool SigControlAndCanPassRedSignal = false;
17455  // doesn't apply for a continuation
17456  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
17457  // EntryPos always 0 for entering at a continuation
17458  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(1),
17459  // at(1) to skip past the Start train value
17460  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
17461  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
17462  // for a train it's the one in front of LeadElement
17463  if(AvTrackSpeed < 30)
17464  AvTrackSpeed = 30;
17465  if(DistanceToRedSignal == -1)
17466  {
17467  TimeToAct = 60.0;
17468  }
17469  else
17470  {
17471  int Speed = AvTrackSpeed;
17472  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
17473  if(AvTrackSpeed > MaxSpeed)
17474  Speed = MaxSpeed;
17475  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(1).SignallerControl)
17476  // defined in timetable as under signaller control
17477  {
17478  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
17479  LaterStopTime = 0;
17480  }
17481  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
17482  // accel & decel taken into account in
17483  // CalcDistanceToRedSignalandStopTime
17484  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
17485  // don't need CurrentStopTime or RecoverableTime for continuation entries
17486  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
17487  TimeToAct += MinsBefEnter;
17488  }
17489  THCandTrainPosParam HCandTrainPosParam;
17490  HCandTrainPosParam.first = HeadCode;
17491  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
17492  // -1-CTE... because 2nd value covers TrainID if +ve &
17493  // continuation track vector position if -ve, -1 allows for vecpos being 0
17494  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
17495  {
17496  OpTimeToActMultiMapEntry.first = TimeToAct;
17497  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
17498  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
17499  }
17500  CTEIt++;
17501  }
17502  }
17503  Utilities->CallLogPop(2081);
17504 }
17505 
17506 // ---------------------------------------------------------------------------
17507 
17508 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
17509  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
17510  float &RecoverableTime, int &AvTrackSpeed)
17511  // new v2.2.0
17512  // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
17513  // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
17514  // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
17515  // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
17516  // aren't used - this means there is no display for the train in question
17517 {
17518  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
17519  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
17520  int DistanceToRedSignal = 0;
17521  int CumTrackSpeed = 0;
17522  // average track speed, in case need to use in time calc
17523  int TrackSpeedCount = 0;
17524 
17525  AvTrackSpeed = 0;
17526  int CurrentElement = TrackVectorPosition;
17527  int CurrentEntryPos = TrackVectorPositionEntryPos;
17528  int NextElement;
17529  int NextEntryPos;
17530  int NextExitPos;
17531 
17532  CurrentStopTime = 0;
17533  LaterStopTime = 0;
17534  RecoverableTime = 0;
17535  if(CurrentElement == -1) // end element, no action needed
17536  {
17537  Utilities->CallLogPop(2094);
17538  return -1;
17539  }
17540  int CurrentExitPos;
17541 
17542  // get ExitPos for first element to be measured
17543  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
17544  {
17545  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
17546  {
17547  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
17548  CurrentExitPos = 1;
17549  else
17550  CurrentExitPos = 3;
17551  }
17552  else
17553  CurrentExitPos = 0; // trailing point
17554  }
17555  else
17556  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
17557  // get CumTrackSpeed for first measured element
17558 
17559  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
17560  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
17561 
17562  // check if currently stopped at a location, and if so add the remaining dwell time
17563  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
17564  if(TrainID > -1)
17565  // -1 for a continuation and can't be at a location as not yet entered
17566  {
17567  TTrain Train = TrainVectorAtIdent(39, TrainID);
17569  // this used to deduct from RecoverableTime when arrive at a location
17570  if(Train.StoppedAtLocation)
17571  {
17572  if(Train.StoppedForTrainInFront)
17573  {
17574  Utilities->CallLogPop(2082);
17575  return -1; // no action needed
17576  }
17577  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
17578  {
17579  Utilities->CallLogPop(2083);
17580  return -1; // not due a departure so no action needed
17581  }
17582  else // due a departure
17583  {
17584  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
17585  // can't convert a TDateTime to a float directly
17586  CurrentStopTime = float(TimeToDepart);
17587  AVPtr++;
17588  }
17589  }
17590  }
17591 
17592  // check if CurrentElement is a red signal, but ok if autosig route after
17593  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
17594  // ok if autosig route after red signal
17595  {
17596  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
17597  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
17598  int RouteNumber; // holder for referenced value, not used
17599  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
17600  {
17601  Utilities->CallLogPop(2078);
17602  return -1;
17603  }
17604  else if(SigControlAndCanPassRedSignal)
17605  // ignore signal and increment CurrentElement to NextElement
17606  {
17607  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
17608  {
17609  if((NextEntryPos == 0) || (NextEntryPos == 2))
17610  // leading entry point
17611  {
17612  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
17613  NextExitPos = 1;
17614  else
17615  NextExitPos = 3;
17616  }
17617  else
17618  NextExitPos = 0; // trailing entry point
17619  }
17620  else
17621  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
17622 
17623  CurrentElement = NextElement;
17624  CurrentEntryPos = NextEntryPos;
17625  CurrentExitPos = NextExitPos;
17626  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
17627  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
17628  }
17629  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
17630  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
17631  {
17632  Utilities->CallLogPop(2084);
17633  return 0;
17634  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
17635  }
17636  }
17637  int LaterStopNumber = 0;
17638  int x = 0;
17639 
17640  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
17641  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
17642  // not red signal next (in fwd direction) so enter loop to calc CumLength
17643  {
17644  x++; // added in v2.4.0 as above
17645  if(x > 5000)
17646  {
17647  Utilities->CallLogPop(2120);
17648  return -1;
17649  }
17650  if(CurrentEntryPos > 1)
17651  {
17652  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
17653  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
17654  }
17655  else
17656  {
17657  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
17658  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
17659  }
17660  TrackSpeedCount++;
17661 
17662  // check for train in front, but if on a bridge on other track then ok
17663  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
17664  int TrainOnElement;
17665  if(TE.TrackType != Bridge)
17666  {
17667  TrainOnElement = TE.TrainIDOnElement;
17668  }
17669  else
17670  {
17671  if(CurrentEntryPos > 1)
17672  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
17673  else
17674  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
17675  }
17676  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
17677  // train in front before red signal
17678  {
17679  Utilities->CallLogPop(2085);
17680  return -1;
17681  }
17682 
17683  // add to stoptime if required
17684  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
17685  {
17686  double StopTimeDouble;
17687  while(AVPtr->FormatType == PassTime)
17688  AVPtr++; // skip past any passes
17689  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
17690  (AVPtr->FormatType == TimeTimeLoc)))
17691  // stop due here so calc dwell time & advance Ptr
17692  {
17693  if(AVPtr->FormatType == TimeTimeLoc)
17694  {
17695  LaterStopNumber++;
17696  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
17697  if(StopTimeDouble < 0.5)
17698  StopTimeDouble = 0.5;
17699  // at least 30 secs delay at station
17700  // can't convert a TDateTime to a float directly
17701  LaterStopTime += float(StopTimeDouble);
17702  RecoverableTime += StopTimeDouble - 0.5;
17703  if((LaterStopNumber == 1) && (TrainID > -1))
17704  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
17705  AVPtr++;
17706  }
17707  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
17708  {
17709  if((AVPtr + 1)->FormatType == TimeLoc)
17710  // must be a departure
17711  {
17712  LaterStopNumber++;
17713  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
17714  // can't convert a TDateTime to a float directly
17715  if(StopTimeDouble < 0.5)
17716  StopTimeDouble = 0.5;
17717  // at least 30 secs delay at station
17718  LaterStopTime += float(StopTimeDouble);
17719  RecoverableTime += StopTimeDouble - 0.5;
17720  if((LaterStopNumber == 1) && (TrainID > -1))
17721  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
17722  AVPtr++;
17723  AVPtr++;
17724  }
17725  else // not a departure, does something else at the location so no calculation needed
17726  {
17727  Utilities->CallLogPop(2086);
17728  return -1;
17729  }
17730  }
17731  }
17732  }
17733 
17734  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
17735  if(NextElement == -1) // reached end element, no action needed
17736  {
17737  Utilities->CallLogPop(2077);
17738  return -1;
17739  }
17740  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
17741  // get NextExitPos
17742  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
17743  {
17744  if((NextEntryPos == 0) || (NextEntryPos == 2))
17745  // leading entry point
17746  {
17747  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
17748  NextExitPos = 1;
17749  else
17750  NextExitPos = 3;
17751  }
17752  else
17753  NextExitPos = 0; // trailing entry point
17754  }
17755  else
17756  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
17757 
17758  CurrentElement = NextElement;
17759  CurrentEntryPos = NextEntryPos;
17760  CurrentExitPos = NextExitPos;
17761  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
17762  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
17763  }
17764  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
17765  // ok if autosig route after red signal, no action needed
17766  {
17767  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
17768  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
17769  int RouteNumber; // holder for referenced value, not used
17770  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
17771  {
17772  Utilities->CallLogPop(2095);
17773  return -1;
17774  }
17775  }
17776  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
17777  // average line speed/2 (for half distance accelerating and half decelerating.
17778 
17779  float MaxAllowableSpeed;
17780 
17781  if(TrackSpeedCount > 0)
17782  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
17783  else // shouldn't reach here but include to prevent divide by zero error
17784  {
17785  if(CurrentEntryPos > 1)
17786  {
17787  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
17788  }
17789  else
17790  {
17791  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
17792  }
17793  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
17794  }
17795  float KmPerLocationStop;
17796 
17797  if(LaterStopNumber > 0)
17798  {
17799  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
17800  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
17801  }
17802  else
17803  {
17804  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
17805  // using linear trendline for accel & decel distance at various speeds
17806  // at half braking, speed never < 60 using this
17807  }
17808  if(AvTrackSpeed > MaxAllowableSpeed)
17809  AvTrackSpeed = MaxAllowableSpeed;
17810  Utilities->CallLogPop(2096);
17811  return DistanceToRedSignal;
17812 }
17813 
17814 // ---------------------------------------------------------------------------
17815 // end of TTrainController entries
17816 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:7640
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:16104
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:106
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:339
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:49
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:13234
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:306
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:305
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:430
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:630
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:62
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1582
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:883
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:144
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:182
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:13257
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:12946
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:327
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:17352
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:738
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:14270
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:6539
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:90
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:50
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:979
Create
@ Create
Definition: TrainUnit.h:49
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:717
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:17113
Arrive
@ Arrive
Definition: TrainUnit.h:49
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:49
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:304
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:345
Depart
@ Depart
Definition: TrainUnit.h:49
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:701
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:10177
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:699
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:383
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:686
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:289
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:6566
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1500
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:50
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:889
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1512
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:624
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:112
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:670
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1523
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:64
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:62
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:447
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:678
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:5729
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:63
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:387
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:66
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:17833
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:12220
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:149
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:327
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:965
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:369
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:384
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:948
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:724
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:300
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:944
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:6774
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:347
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:421
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:327
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:5833
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:724
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:8624
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:307
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:880
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:652
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:14000
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:971
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:550
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:978
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:16901
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:724
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:10132
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1504
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:7612
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:106
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:931
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:674
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:14015
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:235
Intermediate
@ Intermediate
Definition: TrainUnit.h:71
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:929
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:5662
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:945
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3398
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:615
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:62
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:443
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:164
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:8586
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:51
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:436
NotStarted
@ NotStarted
Definition: TrainUnit.h:80
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:9626
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:771
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:720
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:307
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:704
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:335
TTrainDataEntry
Contains all data for a single train.
Definition: TrainUnit.h:177
LeadMid
@ LeadMid
Definition: TrainUnit.h:268
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:764
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:17353
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:13029
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:943
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:5854
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:670
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:985
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:222
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:981
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:185
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:781
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:744
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:660
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:7847
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:14119
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:268
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:817
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:14961
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:14057
StartNew
@ StartNew
Definition: TrainUnit.h:62
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:49
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:268
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:690
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:308
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:50
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:398
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:626
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:16652
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:9740
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:954
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5195
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:450
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:156
TTrain
Definition: TrainUnit.h:271
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:951
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:9759
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:151
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:10234
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:812
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:220
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:794
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:427
GapJump
@ GapJump
Definition: TrackUnit.h:63
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:972
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:766
NoSequence
@ NoSequence
Definition: TrainUnit.h:71
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:147
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:147
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:365
Finish
@ Finish
Definition: TrainUnit.h:71
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:5615
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:8225
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:327
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:137
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1550
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:327
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:321
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:195
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:633
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:325
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:14098
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:761
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:290
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:715
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:409
TExitListIterator
TExitList::iterator TExitListIterator
Definition: TrainUnit.h:88
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:648
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:13822
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1271
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:5426
End
@ End
Definition: TrackUnit.h:73
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:357
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6414
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:7801
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:302
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackElement.at(At)
Definition: TrackUnit.cpp:9367
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:63
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:295
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:14181
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:123
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:939
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:520
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:989
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:770
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:179
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:377
SignalPost
@ SignalPost
Definition: TrackUnit.h:63
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:933
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled action, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:413
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1527
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:310
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:961
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:179
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5012
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:10797
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:120
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:772
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:701
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:821
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:375
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:72
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:423
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:62
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:769
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:738
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:878
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1597
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:427
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:767
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:949
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:62
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:92
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:106
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:427
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:9810
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:927
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:405
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:952
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:9109
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:672
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:327
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:403
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:74
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:977
Pass
@ Pass
Definition: TrainUnit.h:51
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:724
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:686
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:67
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:2881
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
Definition: TrackUnit.cpp:10047
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:754
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:964
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:434
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:704
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:802
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:6845
TExitList
std::list< int > TExitList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:84
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:937
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:646
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:315
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:284
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:970
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:750
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:7624
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:973
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:181
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:62
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:191
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6279
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:741
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:15833
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:415
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:13511
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:9499
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:535
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:278
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3077
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:984
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:411
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4687
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2426
RearSplit
@ RearSplit
Definition: TrainUnit.h:49
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:104
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:51
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:707
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:599
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:934
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:748
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:957
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:133
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:117
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:9402
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2813
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6251
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:13604
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:51
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:772
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:12325
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
new v2.2.0, incremented in Interface.cpp, controls updating for OpTimeToActPanel
Definition: TrainUnit.h:786
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:341
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:217
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3348
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:930
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:292
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:7433
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:6935
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2805
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:753
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:430
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:407
NewService
@ NewService
Definition: TrainUnit.h:49
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:14334
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1367
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:427
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:343
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1511
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:333
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:193
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:189
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:759
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:75
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:974
Crossover
@ Crossover
Definition: TrackUnit.h:63
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4330
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:666
AtLocation
@ AtLocation
Definition: TrainUnit.h:67
Signal
@ Signal
Definition: TrackUnit.h:73
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:936
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:967
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:679
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:151
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:16342
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:800
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:16511
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1512
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:409
Exited
@ Exited
Definition: TrainUnit.h:80
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackElement.at(At)
Definition: TrackUnit.cpp:9354
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:14031
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7167
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:16167
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:986
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2062
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:427
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:55
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1532
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:926
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:427
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:684
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:280
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:726
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:49
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:385
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:738
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:956
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:932
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:327
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:62
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:968
Leave
@ Leave
Definition: TrainUnit.h:49
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:774
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:696
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:722
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
Definition: TrainUnit.h:402
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:355
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2786
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:935
NoFormat
@ NoFormat
Definition: TrainUnit.h:62
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:734
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:62
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2356
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:427
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3377
Enter
@ Enter
Definition: TrainUnit.h:49
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:513
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7050
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:942
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:5795
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:14081
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:396
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:246
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:12864
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:676
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:7882
Timetable
@ Timetable
Definition: TrainUnit.h:56
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:123
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:13190
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6331
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:732
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:9901
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:670
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:102
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:16012
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:724
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:277
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:17252
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:447
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:75
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:391
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:694
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:233
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:730
Terminate
@ Terminate
Definition: TrainUnit.h:49
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:738
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
map of active track element names
Definition: TrackUnit.h:682
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:427
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:145
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:441
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:330
Running
@ Running
Definition: TrainUnit.h:80
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:255
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3468
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:879
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:12192
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:13615
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed)
new v2.2.0, calcs distance to red signal, returns -1 for no signal found, for autosigs route after ne...
Definition: TrainUnit.cpp:17508
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:14357
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:403
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:10122
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:622
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:215
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:775
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:379
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2259
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:17027
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
Definition: TrainUnit.h:790
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1498
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:8311
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:7929
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:8808
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:251
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:959
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:126
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:515
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:7940
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:976
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1584
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:738
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3263
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:131
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:2976
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:409
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:690
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:10218
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:67
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:51
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1496
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:7581
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2849
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:94
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:14203
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:236
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:953
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:13588
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:143
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:792
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:428
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4286
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:248
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:8640
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:50
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:317
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:363
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:10301
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:95
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:7952
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:151
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:885
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:394
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:730
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1565
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:183
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:154
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:128
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:958
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:197
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:145
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:746
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:788
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:518
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:4710
TActionVectorEntry::ExitList
TExitList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:108
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:411
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1502
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1512
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:110
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:296
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:982
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:639
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6087
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:637
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:8295
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:877
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:760
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:313
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:14370
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:657
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:947
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:149
Points
@ Points
Definition: TrackUnit.h:63
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:723
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:353
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:755
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:12767
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6126
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:351
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:738
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:773
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:51
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3326
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:966
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:359
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:445
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:15322
Continuation
@ Continuation
Definition: TrackUnit.h:63
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:724
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:6818
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:655
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:768
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:419
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:8987
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:323
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:16938
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:2940
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:881
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:51
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:707
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:884
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:980
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:8671
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:85
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:425
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:609
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:636
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6030
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:432
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:271
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:294
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:452
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:51
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:963
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:17655
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:137
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:782
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:955
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:198
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:9765
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:282
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:259
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:617
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:648
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:7544
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:61
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:724
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:287
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:693
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:157
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:75
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:706
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:950
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:16803
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:16759
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TExitList &ExitList) const
Check all timetable names in ExitList, if all same return " at [name]" else return ""....
Definition: TrainUnit.cpp:16870
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:728
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:96
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:252
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:71
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:888
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:294
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:48
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:648
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:327
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:763
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:987
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:75
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:8265
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:13535
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:6588
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:16026
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:371
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5064
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:16269
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:8280
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:118
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:12199
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:303
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:7688
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:14163
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:155
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:67
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:898
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:349
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:187
NoMode
@ NoMode
Definition: TrainUnit.h:56
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:765
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:10647
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:98
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:697
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:738
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:62
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:146
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:210
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:783
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:758
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:736
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:457
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:4793
TTrainController::OperatingTrainArrDep
int OperatingTrainArrDep
< all these set to 0 in constructor
Definition: TrainUnit.h:777
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:427
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:8659
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:946
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:139
TTrainController::NotStartedTrainArrDep
int NotStartedTrainArrDep
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:779
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:267
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:709
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable (the object is a member of TTrainController)
Definition: TrainUnit.h:210
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:13222
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:6650
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:400
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:635
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1506
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:114
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:319
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:699
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:940
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:983
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:695
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:2907
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled action entries, null on creation
Definition: TrainUnit.h:96
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:116
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:367
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:975
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:96
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4442
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:668
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6186
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:147
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:361
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:141
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:738
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:938
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:5689
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:708
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:62
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:50
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:698
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:17206
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:111
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:664
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:62
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:752
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:969
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:796
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:650
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:98
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:4699
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:389
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:147
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:654
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:298
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:645
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:712
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:63
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:762
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2831
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:87
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:604
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1036
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:960
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:16552
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6058
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3300
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:100
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2439
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:179
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6362
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:301
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:73
Repeat
@ Repeat
Definition: TrainUnit.h:63
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:928
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:12208
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:588
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:738
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:156
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:64
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:428
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:962
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:641
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:807
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:218
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5340
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:16295
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:141
Signaller
@ Signaller
Definition: TrainUnit.h:56
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:438
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:886
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:153
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:875
Bridge
@ Bridge
Definition: TrackUnit.h:63
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:941
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:250
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:327
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:70
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:381
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:71
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:523
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:63
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:104
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:96